\n \n ),\n });\n\n // Clear any existing static resources from the global scope to attempt to prevent leaking between pages.\n // This could break if pages are rendered in parallel or if fonts are loaded outside of the React tree\n Font.resetServerContext();\n\n // This MUST be run before `ReactDOMServer.renderToString` to prevent\n // \"Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.\"\n resetReactNavigationContexts();\n\n const loadedData =\n options?.loader?.data !== undefined ? { [location.pathname]: options.loader.data } : null;\n\n const html = ReactDOMServer.renderToString(\n \n {element}\n \n );\n\n // Eval the CSS after the HTML is rendered so that the CSS is in the same order\n const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());\n\n let output = mixHeadComponentsWithStaticResults(headContext.helmet, html);\n\n output = output.replace('', `${css}`);\n\n const fonts = Font.getServerResources();\n debug(`Pushing static fonts: (count: ${fonts.length})`, fonts);\n // debug('Push static fonts:', fonts)\n // Inject static fonts loaded with expo-font\n output = output.replace('', `${fonts.join('')}`);\n if (loadedData) {\n const loaderDataScript = ReactDOMServer.renderToStaticMarkup(\n \n );\n output = output.replace('', `${loaderDataScript}`);\n }\n\n // Inject hydration assets (JS/CSS bundles). Used in SSR mode\n if (options?.assets) {\n if (options.assets.css.length > 0) {\n /**\n * For each CSS file, inject two link elements; one for preloading and one as the actual\n * stylesheet. This matches what we do for SSG\n *\n * @see @expo/cli/src/start/server/metro/serializeHtml.ts\n */\n const injectedCSS = options.assets.css\n .flatMap((href) => [\n ``,\n ``,\n ])\n .join('\\n');\n output = output.replace('', `${injectedCSS}\\n`);\n }\n\n if (options.assets.js.length > 0) {\n const injectedJS = options.assets.js\n .map((src) => ``)\n .join('\\n');\n output = output.replace('
', `${injectedJS}\\n`);\n }\n }\n\n return '' + output;\n}\n\nfunction mixHeadComponentsWithStaticResults(helmet: any, html: string) {\n // Head components\n for (const key of ['title', 'priority', 'meta', 'link', 'script', 'style'].reverse()) {\n const result = helmet?.[key]?.toString();\n if (result) {\n html = html.replace('
', `
${result}`);\n }\n }\n\n // attributes\n html = html.replace('>();\n}\n\nexport type GetStaticContentOptions = {\n loader?: {\n data?: any;\n };\n request?: Request;\n /** Asset manifest for hydration bundles (JS/CSS). Used in SSR. */\n assets?: {\n css: string[];\n js: string[];\n };\n};\n\nexport async function getStaticContent(\n location: URL,\n options?: GetStaticContentOptions\n): Promise {\n const headContext: { helmet?: any } = {};\n const Root = getRootComponent();\n\n const {\n // NOTE: The `element` that's returned adds two extra Views and\n // the seemingly unused `RootTagContext.Provider`.\n element,\n getStyleElement,\n } = registerStaticRootComponent(ExpoRoot, {\n location,\n context: ctx,\n wrapper: ({ children }: React.ComponentProps) => (\n \n
{children}
\n \n ),\n });\n\n // Clear any existing static resources from the global scope to attempt to prevent leaking between pages.\n // This could break if pages are rendered in parallel or if fonts are loaded outside of the React tree\n Font.resetServerContext();\n\n // This MUST be run before `ReactDOMServer.renderToString` to prevent\n // \"Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.\"\n resetReactNavigationContexts();\n\n const loadedData =\n options?.loader?.data !== undefined\n ? { [location.pathname + location.search]: options.loader.data }\n : null;\n\n const html = ReactDOMServer.renderToString(\n \n {element}\n \n );\n\n // Eval the CSS after the HTML is rendered so that the CSS is in the same order\n const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());\n\n let output = mixHeadComponentsWithStaticResults(headContext.helmet, html);\n\n output = output.replace('', `${css}`);\n\n const fonts = Font.getServerResources();\n debug(`Pushing static fonts: (count: ${fonts.length})`, fonts);\n // debug('Push static fonts:', fonts)\n // Inject static fonts loaded with expo-font\n output = output.replace('', `${fonts.join('')}`);\n if (loadedData) {\n const loaderDataScript = ReactDOMServer.renderToStaticMarkup(\n \n );\n output = output.replace('', `${loaderDataScript}`);\n }\n\n // Inject hydration assets (JS/CSS bundles). Used in SSR mode\n if (options?.assets) {\n if (options.assets.css.length > 0) {\n /**\n * For each CSS file, inject two link elements; one for preloading and one as the actual\n * stylesheet. This matches what we do for SSG\n *\n * @see @expo/cli/src/start/server/metro/serializeHtml.ts\n */\n const injectedCSS = options.assets.css\n .flatMap((href) => [\n ``,\n ``,\n ])\n .join('\\n');\n output = output.replace('', `${injectedCSS}\\n`);\n }\n\n if (options.assets.js.length > 0) {\n const injectedJS = options.assets.js\n .map((src) => ``)\n .join('\\n');\n output = output.replace('', `${injectedJS}\\n`);\n }\n }\n\n return '' + output;\n}\n\nfunction mixHeadComponentsWithStaticResults(helmet: any, html: string) {\n // Head components\n for (const key of ['title', 'priority', 'meta', 'link', 'script', 'style'].reverse()) {\n const result = helmet?.[key]?.toString();\n if (result) {\n html = html.replace('', `${result}`);\n }\n }\n\n // attributes\n html = html.replace('
diff --git a/packages/expo-brownfield/CHANGELOG.md b/packages/expo-brownfield/CHANGELOG.md
index 9ebed32d06db29..3473458b08aff5 100644
--- a/packages/expo-brownfield/CHANGELOG.md
+++ b/packages/expo-brownfield/CHANGELOG.md
@@ -18,3 +18,4 @@
- Initialized the package for `expo-brownfield` with code from [expo-brownfield-target](https://github.com/software-mansion-labs/expo-brownfield-target) in [#42012](https://github.com/expo/expo/pull/42012) by [@pmleczek](https://github.com/pmleczek), [@gabrieldonadel](https://github.com/gabrieldonadel)
- Updated `minimal-tester` to use `expo-brownfield` (includes 2 minor iOS improvments in the package) in [#42048](https://github.com/expo/expo/pull/42048) by [@gabrieldonadel](https://github.com/gabrieldonadel)
- Updated build configurations and resolved leftover TODOs in [#42072](https://github.com/expo/expo/pull/42072) by [@pmleczek](https://github.com/pmleczek)
+- [iOS] Symlink ExpoAppDelegate to iOS template ([#42240](https://github.com/expo/expo/pull/42240) by [@gabrieldonadel](https://github.com/gabrieldonadel))
diff --git a/packages/expo-brownfield/package.json b/packages/expo-brownfield/package.json
index a00aad0277892a..9abbabcdfa3db8 100644
--- a/packages/expo-brownfield/package.json
+++ b/packages/expo-brownfield/package.json
@@ -22,6 +22,7 @@
"lint:fix": "expo-module lint --fix && expo-module lint plugin --fix && expo-module lint cli --fix",
"test": "expo-module test",
"prepare": "expo-module prepare",
+ "prepack": "cp -L plugin/templates/ios/ExpoAppDelegate.swift plugin/templates/ios/ExpoAppDelegate.swift.tmp && mv plugin/templates/ios/ExpoAppDelegate.swift.tmp plugin/templates/ios/ExpoAppDelegate.swift",
"prepublishOnly": "expo-module prepublishOnly",
"expo-module": "expo-module"
},
@@ -47,11 +48,13 @@
"dependencies": {
"arg": "^5.0.2",
"chalk": "^4.1.2",
+ "diff": "^5.2.0",
"expo-build-properties": "~1.0.8",
"ora": "^5.4.1",
"prompts": "^2.4.2"
},
"devDependencies": {
+ "@types/diff": "^5.2.0",
"expo-module-scripts": "^5.0.7",
"jest-expo": "~54.0.11"
},
diff --git a/packages/expo-brownfield/plugin/build/common/filesystem.d.ts b/packages/expo-brownfield/plugin/build/common/filesystem.d.ts
index 4b2a44bf766fa4..f96d6eb545032d 100644
--- a/packages/expo-brownfield/plugin/build/common/filesystem.d.ts
+++ b/packages/expo-brownfield/plugin/build/common/filesystem.d.ts
@@ -3,3 +3,9 @@ export declare const mkdir: (path: string, recursive?: boolean) => void;
export declare const createFileFromTemplate: (template: string, at: string, platform?: PlatformString, variables?: Record) => void;
export declare const createFileFromTemplateAs: (template: string, at: string, as: string, platform?: PlatformString, variables?: Record) => void;
export declare const readFromTemplate: (template: string, platform?: PlatformString, variables?: Record) => string;
+/**
+ * Applies a unified diff patch to a file.
+ * @param patchFile - The name of the patch file in the patches directory
+ * @param targetFilePath - The absolute path to the file to patch
+ */
+export declare const applyPatchToFile: (patchFile: string, targetFilePath: string) => void;
diff --git a/packages/expo-brownfield/plugin/build/common/filesystem.js b/packages/expo-brownfield/plugin/build/common/filesystem.js
index 7256c359dc2d09..9325b769c1e82a 100644
--- a/packages/expo-brownfield/plugin/build/common/filesystem.js
+++ b/packages/expo-brownfield/plugin/build/common/filesystem.js
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
-exports.readFromTemplate = exports.createFileFromTemplateAs = exports.createFileFromTemplate = exports.mkdir = void 0;
+exports.applyPatchToFile = exports.readFromTemplate = exports.createFileFromTemplateAs = exports.createFileFromTemplate = exports.mkdir = void 0;
+const diff_1 = require("diff");
const node_fs_1 = require("node:fs");
const node_path_1 = __importDefault(require("node:path"));
const mkdir = (path, recursive = false) => {
@@ -74,3 +75,25 @@ const readFromTemplate = (template, platform, variables) => {
return templateContents;
};
exports.readFromTemplate = readFromTemplate;
+/**
+ * Applies a unified diff patch to a file.
+ * @param patchFile - The name of the patch file in the patches directory
+ * @param targetFilePath - The absolute path to the file to patch
+ */
+const applyPatchToFile = (patchFile, targetFilePath) => {
+ const patchPath = node_path_1.default.join(__filename, '../../..', 'templates', 'patches', patchFile);
+ if (!(0, node_fs_1.existsSync)(patchPath)) {
+ throw new Error(`Patch file ${patchFile} doesn't exist at ${patchPath}`);
+ }
+ if (!(0, node_fs_1.existsSync)(targetFilePath)) {
+ throw new Error(`Target file doesn't exist at ${targetFilePath}`);
+ }
+ const patchContent = (0, node_fs_1.readFileSync)(patchPath, 'utf-8');
+ const originalContent = (0, node_fs_1.readFileSync)(targetFilePath, 'utf-8');
+ const patchedContent = (0, diff_1.applyPatch)(originalContent, patchContent);
+ if (patchedContent === false) {
+ throw new Error(`Failed to apply patch ${patchFile} to ${targetFilePath}`);
+ }
+ (0, node_fs_1.writeFileSync)(targetFilePath, patchedContent);
+};
+exports.applyPatchToFile = applyPatchToFile;
diff --git a/packages/expo-brownfield/plugin/build/ios/plugins/withXcodeProjectPlugin.js b/packages/expo-brownfield/plugin/build/ios/plugins/withXcodeProjectPlugin.js
index f2ba90d0a4a604..6e442ab9fccfac 100644
--- a/packages/expo-brownfield/plugin/build/ios/plugins/withXcodeProjectPlugin.js
+++ b/packages/expo-brownfield/plugin/build/ios/plugins/withXcodeProjectPlugin.js
@@ -16,27 +16,26 @@ const withXcodeProjectPlugin = (config, pluginConfig) => {
// Create a directory for the framework files
const groupPath = node_path_1.default.join(projectRoot, 'ios', pluginConfig.targetName);
(0, utils_1.mkdir)(groupPath);
- // Create the React Native host manager based on the template
- (0, utils_1.createFileFromTemplate)('ReactNativeHostManager.swift', groupPath);
- // Create the messaging proxy based on the template
- (0, utils_1.createFileFromTemplate)('Messaging.swift', groupPath);
- // Create the SwiftUI brownfield entrypoint based on the template
- (0, utils_1.createFileFromTemplate)('ReactNativeView.swift', groupPath);
- // Create the UIKit brownfield view controller based on the template
- (0, utils_1.createFileFromTemplate)('ReactNativeViewController.swift', groupPath);
- // Create the BrownfieldAppDelegate based on the template
- (0, utils_1.createFileFromTemplate)('BrownfieldAppDelegate.swift', groupPath);
- // Create the ReactNativeDelegate based on the template
- (0, utils_1.createFileFromTemplate)('ReactNativeDelegate.swift', groupPath);
- // Create and properly add a new group for the framework
- (0, utils_1.createGroup)(xcodeProject, pluginConfig.targetName, groupPath, [
+ const templateFiles = [
+ // React Native host manager
'ReactNativeHostManager.swift',
+ // Messaging proxy
'Messaging.swift',
+ //SwiftUI brownfield entrypoint
'ReactNativeView.swift',
+ // UIKit brownfield view controller
'ReactNativeViewController.swift',
- 'BrownfieldAppDelegate.swift',
+ // ExpoAppDelegate symlinked and reexported from the main Expo package
+ 'ExpoAppDelegate.swift',
+ // ReactNativeDelegate
'ReactNativeDelegate.swift',
- ]);
+ ];
+ // Create files from templates
+ templateFiles.forEach((templateFile) => (0, utils_1.createFileFromTemplate)(templateFile, groupPath));
+ // Apply patch to ExpoAppDelegate.swift to make it compatible with the brownfield framework
+ (0, utils_1.applyPatchToFile)('ExpoAppDelegate.patch', node_path_1.default.join(groupPath, 'ExpoAppDelegate.swift'));
+ // Create and properly add a new group for the framework
+ (0, utils_1.createGroup)(xcodeProject, pluginConfig.targetName, groupPath, templateFiles);
// Create 'Info.plist' and '.entitlements' based on the templates
(0, utils_1.createFileFromTemplate)('Info.plist', groupPath, {
bundleIdentifier: pluginConfig.bundleIdentifier,
@@ -46,18 +45,8 @@ const withXcodeProjectPlugin = (config, pluginConfig) => {
// Configure build phases:
// - Reference Expo app target's RN bundle script
// - Add custom script for patching ExpoModulesProvider
- // - Add 'ReactNativeHostManager.swift', 'ReactNativeView.swift',
- // 'Messaging.swift', 'ReactNativeViewController.swift' and
- // 'BrownfieldAppDelegate.swift'
- // to the compile sources phase
- (0, utils_1.configureBuildPhases)(xcodeProject, target, pluginConfig.targetName, projectName, [
- `${pluginConfig.targetName}/ReactNativeHostManager.swift`,
- `${pluginConfig.targetName}/Messaging.swift`,
- `${pluginConfig.targetName}/ReactNativeView.swift`,
- `${pluginConfig.targetName}/ReactNativeViewController.swift`,
- `${pluginConfig.targetName}/BrownfieldAppDelegate.swift`,
- `${pluginConfig.targetName}/ReactNativeDelegate.swift`,
- ]);
+ // - Add template files to the compile sources phase
+ (0, utils_1.configureBuildPhases)(xcodeProject, target, pluginConfig.targetName, projectName, templateFiles.map((file) => `${pluginConfig.targetName}/${file}`));
// Add the required build settings
(0, utils_1.configureBuildSettings)(xcodeProject, pluginConfig.targetName, config.ios?.buildNumber || '1', pluginConfig.bundleIdentifier);
return config;
diff --git a/packages/expo-brownfield/plugin/build/ios/utils/filesystem.d.ts b/packages/expo-brownfield/plugin/build/ios/utils/filesystem.d.ts
index a7424836be562e..2640b3c9e5b13b 100644
--- a/packages/expo-brownfield/plugin/build/ios/utils/filesystem.d.ts
+++ b/packages/expo-brownfield/plugin/build/ios/utils/filesystem.d.ts
@@ -1,3 +1,5 @@
+import { applyPatchToFile as applyPatchToFileCommon } from '../../common/filesystem';
+export { applyPatchToFileCommon as applyPatchToFile };
export declare const mkdir: (path: string, recursive?: boolean) => void;
export declare const createFileFromTemplate: (template: string, at: string, variables?: Record) => void;
export declare const createFileFromTemplateAs: (template: string, at: string, as: string, variables?: Record) => void;
diff --git a/packages/expo-brownfield/plugin/build/ios/utils/filesystem.js b/packages/expo-brownfield/plugin/build/ios/utils/filesystem.js
index 7bd7abb8805c80..ca45440d071cb1 100644
--- a/packages/expo-brownfield/plugin/build/ios/utils/filesystem.js
+++ b/packages/expo-brownfield/plugin/build/ios/utils/filesystem.js
@@ -3,9 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
-exports.readFromTemplate = exports.createFileFromTemplateAs = exports.createFileFromTemplate = exports.mkdir = void 0;
+exports.readFromTemplate = exports.createFileFromTemplateAs = exports.createFileFromTemplate = exports.mkdir = exports.applyPatchToFile = void 0;
const node_fs_1 = __importDefault(require("node:fs"));
const filesystem_1 = require("../../common/filesystem");
+Object.defineProperty(exports, "applyPatchToFile", { enumerable: true, get: function () { return filesystem_1.applyPatchToFile; } });
const mkdir = (path, recursive = false) => {
node_fs_1.default.mkdirSync(path, {
recursive,
diff --git a/packages/expo-brownfield/plugin/src/common/filesystem.ts b/packages/expo-brownfield/plugin/src/common/filesystem.ts
index 1f067fbcd1bc2b..dfbcceaeb05b2d 100644
--- a/packages/expo-brownfield/plugin/src/common/filesystem.ts
+++ b/packages/expo-brownfield/plugin/src/common/filesystem.ts
@@ -1,3 +1,4 @@
+import { applyPatch } from 'diff';
import { accessSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
@@ -104,3 +105,31 @@ export const readFromTemplate = (
return templateContents;
};
+
+/**
+ * Applies a unified diff patch to a file.
+ * @param patchFile - The name of the patch file in the patches directory
+ * @param targetFilePath - The absolute path to the file to patch
+ */
+export const applyPatchToFile = (patchFile: string, targetFilePath: string) => {
+ const patchPath = path.join(__filename, '../../..', 'templates', 'patches', patchFile);
+
+ if (!existsSync(patchPath)) {
+ throw new Error(`Patch file ${patchFile} doesn't exist at ${patchPath}`);
+ }
+
+ if (!existsSync(targetFilePath)) {
+ throw new Error(`Target file doesn't exist at ${targetFilePath}`);
+ }
+
+ const patchContent = readFileSync(patchPath, 'utf-8');
+ const originalContent = readFileSync(targetFilePath, 'utf-8');
+
+ const patchedContent = applyPatch(originalContent, patchContent);
+
+ if (patchedContent === false) {
+ throw new Error(`Failed to apply patch ${patchFile} to ${targetFilePath}`);
+ }
+
+ writeFileSync(targetFilePath, patchedContent);
+};
diff --git a/packages/expo-brownfield/plugin/src/ios/plugins/withXcodeProjectPlugin.ts b/packages/expo-brownfield/plugin/src/ios/plugins/withXcodeProjectPlugin.ts
index a6994279a4dcd4..89794b3ee81664 100644
--- a/packages/expo-brownfield/plugin/src/ios/plugins/withXcodeProjectPlugin.ts
+++ b/packages/expo-brownfield/plugin/src/ios/plugins/withXcodeProjectPlugin.ts
@@ -3,6 +3,7 @@ import path from 'node:path';
import type { PluginConfig } from '../types';
import {
+ applyPatchToFile,
configureBuildPhases,
configureBuildSettings,
createFileFromTemplate,
@@ -30,28 +31,30 @@ const withXcodeProjectPlugin: ConfigPlugin = (config, pluginConfig
// Create a directory for the framework files
const groupPath = path.join(projectRoot, 'ios', pluginConfig.targetName);
mkdir(groupPath);
- // Create the React Native host manager based on the template
- createFileFromTemplate('ReactNativeHostManager.swift', groupPath);
- // Create the messaging proxy based on the template
- createFileFromTemplate('Messaging.swift', groupPath);
- // Create the SwiftUI brownfield entrypoint based on the template
- createFileFromTemplate('ReactNativeView.swift', groupPath);
- // Create the UIKit brownfield view controller based on the template
- createFileFromTemplate('ReactNativeViewController.swift', groupPath);
- // Create the BrownfieldAppDelegate based on the template
- createFileFromTemplate('BrownfieldAppDelegate.swift', groupPath);
- // Create the ReactNativeDelegate based on the template
- createFileFromTemplate('ReactNativeDelegate.swift', groupPath);
- // Create and properly add a new group for the framework
- createGroup(xcodeProject, pluginConfig.targetName, groupPath, [
+ const templateFiles = [
+ // React Native host manager
'ReactNativeHostManager.swift',
+ // Messaging proxy
'Messaging.swift',
+ //SwiftUI brownfield entrypoint
'ReactNativeView.swift',
+ // UIKit brownfield view controller
'ReactNativeViewController.swift',
- 'BrownfieldAppDelegate.swift',
+ // ExpoAppDelegate symlinked and reexported from the main Expo package
+ 'ExpoAppDelegate.swift',
+ // ReactNativeDelegate
'ReactNativeDelegate.swift',
- ]);
+ ];
+
+ // Create files from templates
+ templateFiles.forEach((templateFile) => createFileFromTemplate(templateFile, groupPath));
+
+ // Apply patch to ExpoAppDelegate.swift to make it compatible with the brownfield framework
+ applyPatchToFile('ExpoAppDelegate.patch', path.join(groupPath, 'ExpoAppDelegate.swift'));
+
+ // Create and properly add a new group for the framework
+ createGroup(xcodeProject, pluginConfig.targetName, groupPath, templateFiles);
// Create 'Info.plist' and '.entitlements' based on the templates
createFileFromTemplate('Info.plist', groupPath, {
@@ -67,18 +70,14 @@ const withXcodeProjectPlugin: ConfigPlugin = (config, pluginConfig
// Configure build phases:
// - Reference Expo app target's RN bundle script
// - Add custom script for patching ExpoModulesProvider
- // - Add 'ReactNativeHostManager.swift', 'ReactNativeView.swift',
- // 'Messaging.swift', 'ReactNativeViewController.swift' and
- // 'BrownfieldAppDelegate.swift'
- // to the compile sources phase
- configureBuildPhases(xcodeProject, target, pluginConfig.targetName, projectName, [
- `${pluginConfig.targetName}/ReactNativeHostManager.swift`,
- `${pluginConfig.targetName}/Messaging.swift`,
- `${pluginConfig.targetName}/ReactNativeView.swift`,
- `${pluginConfig.targetName}/ReactNativeViewController.swift`,
- `${pluginConfig.targetName}/BrownfieldAppDelegate.swift`,
- `${pluginConfig.targetName}/ReactNativeDelegate.swift`,
- ]);
+ // - Add template files to the compile sources phase
+ configureBuildPhases(
+ xcodeProject,
+ target,
+ pluginConfig.targetName,
+ projectName,
+ templateFiles.map((file) => `${pluginConfig.targetName}/${file}`)
+ );
// Add the required build settings
configureBuildSettings(
xcodeProject,
diff --git a/packages/expo-brownfield/plugin/src/ios/utils/filesystem.ts b/packages/expo-brownfield/plugin/src/ios/utils/filesystem.ts
index 56119e8322c27f..0aeb7cb5b882c2 100644
--- a/packages/expo-brownfield/plugin/src/ios/utils/filesystem.ts
+++ b/packages/expo-brownfield/plugin/src/ios/utils/filesystem.ts
@@ -1,11 +1,14 @@
import fs from 'node:fs';
import {
+ applyPatchToFile as applyPatchToFileCommon,
createFileFromTemplate as createFileFromTemplateCommon,
createFileFromTemplateAs as createFileFromTemplateAsCommon,
readFromTemplate as readFromTemplateCommon,
} from '../../common/filesystem';
+export { applyPatchToFileCommon as applyPatchToFile };
+
export const mkdir = (path: string, recursive: boolean = false) => {
fs.mkdirSync(path, {
recursive,
diff --git a/packages/expo-brownfield/plugin/templates/ios/BrownfieldAppDelegate.swift b/packages/expo-brownfield/plugin/templates/ios/BrownfieldAppDelegate.swift
deleted file mode 100644
index e3e8c9bbe5b6b5..00000000000000
--- a/packages/expo-brownfield/plugin/templates/ios/BrownfieldAppDelegate.swift
+++ /dev/null
@@ -1,171 +0,0 @@
-internal import ExpoModulesCore
-import UIKit
-
-@objc
-open class BrownfieldAppDelegate: UIResponder, UIApplicationDelegate {
- // TODO(pmleczek): Add shared instance to enable using single methods (?)
-
- // SECTION: Initializing the app
- open func application(
- _ application: UIApplication,
- willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
- ) -> Bool {
- ExpoAppDelegateSubscriberManager.application(
- application, willFinishLaunchingWithOptions: launchOptions)
- }
-
- open func application(
- _ application: UIApplication,
- didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
- ) -> Bool {
- ExpoAppDelegateSubscriberManager.application(
- application, didFinishLaunchingWithOptions: launchOptions)
- }
- // END SECTION: Initializing the app
-
- // SECTION: Responding to App Life-Cycle Events
- open func applicationDidBecomeActive(_ application: UIApplication) {
- ExpoAppDelegateSubscriberManager.applicationDidBecomeActive(application)
- }
-
- open func applicationWillResignActive(_ application: UIApplication) {
- ExpoAppDelegateSubscriberManager.applicationWillResignActive(application)
- }
-
- open func applicationDidEnterBackground(_ application: UIApplication) {
- ExpoAppDelegateSubscriberManager.applicationDidEnterBackground(application)
- }
-
- open func applicationWillEnterForeground(_ application: UIApplication) {
- ExpoAppDelegateSubscriberManager.applicationWillEnterForeground(application)
- }
-
- open func applicationWillTerminate(_ application: UIApplication) {
- ExpoAppDelegateSubscriberManager.applicationWillTerminate(application)
- }
- // END SECTION: Responding to App Life-Cycle Events
-
- // SECTION: Responding to Environment Changes
- open func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
- ExpoAppDelegateSubscriberManager.applicationDidReceiveMemoryWarning(application)
- }
- // END SECTION: Responding to Environment Changes
-
- // SECTION: Downloading Data in the Background
- open func application(
- _ application: UIApplication,
- handleEventsForBackgroundURLSession identifier: String,
- completionHandler: @escaping () -> Void
- ) {
- ExpoAppDelegateSubscriberManager.application(
- application,
- handleEventsForBackgroundURLSession: identifier,
- completionHandler: completionHandler
- )
- }
- // END SECTION: Downloading Data in the Background
-
- // SECTION: Handling Remote Notification Registration
- open func application(
- _ application: UIApplication,
- didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
- ) {
- ExpoAppDelegateSubscriberManager.application(
- application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
- }
-
- open func application(
- _ application: UIApplication,
- didFailToRegisterForRemoteNotificationsWithError error: Error
- ) {
- ExpoAppDelegateSubscriberManager.application(
- application, didFailToRegisterForRemoteNotificationsWithError: error)
- }
-
- open func application(
- _ application: UIApplication,
- didReceiveRemoteNotification userInfo: [AnyHashable: Any],
- fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
- ) {
- ExpoAppDelegateSubscriberManager.application(
- application,
- didReceiveRemoteNotification: userInfo,
- fetchCompletionHandler: completionHandler
- )
- }
- // END SECTION: Handling Remote Notification Registration
-
- // SECTION: Continuing User Activity and Handling Quick Actions
- open func application(
- _ application: UIApplication,
- willContinueUserActivityWithType userActivityType: String
- ) -> Bool {
- ExpoAppDelegateSubscriberManager.application(
- application, willContinueUserActivityWithType: userActivityType)
- }
-
- open func application(
- _ application: UIApplication,
- continue userActivity: NSUserActivity,
- restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
- ) -> Bool {
- ExpoAppDelegateSubscriberManager.application(
- application, continue: userActivity, restorationHandler: restorationHandler)
- }
-
- open func application(
- _ application: UIApplication,
- didUpdate userActivity: NSUserActivity
- ) {
- ExpoAppDelegateSubscriberManager.application(application, didUpdate: userActivity)
- }
-
- open func application(
- _ application: UIApplication,
- didFailToContinueUserActivityWithType userActivityType: String,
- error: Error
- ) {
- ExpoAppDelegateSubscriberManager.application(
- application, didFailToContinueUserActivityWithType: userActivityType, error: error)
- }
-
- open func application(
- _ application: UIApplication,
- performActionFor shortcutItem: UIApplicationShortcutItem,
- completionHandler: @escaping (Bool) -> Void
- ) {
- ExpoAppDelegateSubscriberManager.application(
- application, performActionFor: shortcutItem, completionHandler: completionHandler)
- }
- // END SECTION: Continuing User Activity and Handling Quick Actions
-
- // SECTION: Background Fetch
- open func application(
- _ application: UIApplication,
- performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
- ) {
- ExpoAppDelegateSubscriberManager.application(
- application, performFetchWithCompletionHandler: completionHandler)
- }
- // END SECTION: Background Fetch
-
- // SECTION: Opening a URL-Specified Resource
- open func application(
- _ app: UIApplication,
- open url: URL,
- options: [UIApplication.OpenURLOptionsKey: Any] = [:]
- ) -> Bool {
- ExpoAppDelegateSubscriberManager.application(app, open: url, options: options)
- }
- // END SECTION: Opening a URL-Specified Resource
-
- // SECTION: Managing Interface Geometry
- open func application(
- _ application: UIApplication,
- supportedInterfaceOrientationsFor window: UIWindow?
- ) -> UIInterfaceOrientationMask {
- ExpoAppDelegateSubscriberManager.application(
- application, supportedInterfaceOrientationsFor: window)
- }
- // END SECTION: Managing Interface Geometry
-}
diff --git a/packages/expo-brownfield/plugin/templates/ios/ExpoAppDelegate.swift b/packages/expo-brownfield/plugin/templates/ios/ExpoAppDelegate.swift
new file mode 120000
index 00000000000000..ec8bccd5cb2af5
--- /dev/null
+++ b/packages/expo-brownfield/plugin/templates/ios/ExpoAppDelegate.swift
@@ -0,0 +1 @@
+/Users/gabriel/Workspace/expo/expo/packages/expo/ios/AppDelegates/ExpoAppDelegate.swift
\ No newline at end of file
diff --git a/packages/expo-brownfield/plugin/templates/patches/ExpoAppDelegate.patch b/packages/expo-brownfield/plugin/templates/patches/ExpoAppDelegate.patch
new file mode 100644
index 00000000000000..7ec819ce5fb64e
--- /dev/null
+++ b/packages/expo-brownfield/plugin/templates/patches/ExpoAppDelegate.patch
@@ -0,0 +1,23 @@
+diff --git a/plugin/templates/ios/ExpoAppDelegate.swift b/plugin/templates/ios/ExpoAppDelegate.swift
+index 09766c71fd8..f1e5a19d7cd 100644
+--- a/plugin/templates/ios/ExpoAppDelegate.swift
++++ b/plugin/templates/ios/ExpoAppDelegate.swift
+@@ -1,5 +1,5 @@
+-import Foundation
+-import ExpoModulesCore
++import UIKit
++internal import ExpoModulesCore
+
+ /**
+ Allows classes extending `ExpoAppDelegateSubscriber` to hook into project's app delegate
+@@ -7,8 +7,8 @@ import ExpoModulesCore
+
+ Keep functions and markers in sync with https://developer.apple.com/documentation/uikit/uiapplicationdelegate
+ */
+-@objc(EXExpoAppDelegate)
+-open class ExpoAppDelegate: UIResponder, UIApplicationDelegate {
++@objc
++open class ExpoBrownfieldAppDelegate: UIResponder, UIApplicationDelegate {
+ override public init() {
+ // The subscribers are initializing and registering before the main code starts executing.
+ // Here we're letting them know when the `AppDelegate` is being created,
diff --git a/packages/expo-modules-autolinking/scripts/ios/autolinking_manager.rb b/packages/expo-modules-autolinking/scripts/ios/autolinking_manager.rb
index 690e1a9c58998a..cf2123dd9cd9c1 100644
--- a/packages/expo-modules-autolinking/scripts/ios/autolinking_manager.rb
+++ b/packages/expo-modules-autolinking/scripts/ios/autolinking_manager.rb
@@ -172,9 +172,7 @@ class AutolinkingManager
return @options.fetch(:appRoot, @options.fetch(:projectRoot, nil))
end
- # privates
-
- private def resolve
+ public def resolve
json = []
IO.popen(resolve_command_args) do |data|
diff --git a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/AppContext.kt b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/AppContext.kt
index 18cb18a1fb5834..28165ce11c1a7f 100644
--- a/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/AppContext.kt
+++ b/packages/expo-modules-core/android/src/main/java/expo/modules/kotlin/AppContext.kt
@@ -129,6 +129,8 @@ class AppContext(
registry.register(modulesProvider)
+ registerInlineModulesList()
+
logger.info("✅ AppContext was initialized")
}
}
@@ -137,6 +139,14 @@ class AppContext(
registry.postOnCreate()
}
+ private fun registerInlineModulesList() {
+ try {
+ val inlineModulesList = Class.forName("inline.modules.ExpoInlineModulesList").getConstructor()
+ .newInstance() as ModulesProvider
+ registry.register(inlineModulesList)
+ } catch (_: ClassNotFoundException) {}
+ }
+
/**
* Initializes a JSI part of the module registry.
* It will be a NOOP if the remote debugging was activated.
diff --git a/packages/expo-router/CHANGELOG.md b/packages/expo-router/CHANGELOG.md
index 8f1d4c579ba6ce..39b715c391e29f 100644
--- a/packages/expo-router/CHANGELOG.md
+++ b/packages/expo-router/CHANGELOG.md
@@ -69,6 +69,7 @@
- add replace action handling to headless tabs ([#41815](https://github.com/expo/expo/pull/41815) by [@Ubax](https://github.com/Ubax))
- [ios] fix build error 'Logger' is ambiguous ([#42229](https://github.com/expo/expo/pull/42229) by [@Ubax](https://github.com/Ubax))
- [ios] fix shadow color in native tabs ([#42125](https://github.com/expo/expo/pull/42125) by [@Ubax](https://github.com/Ubax))
+- Preserve search params for loader data fetches ([#42227](https://github.com/expo/expo/pull/42227) by [@hassankhan](https://github.com/hassankhan))
### 💡 Others
@@ -110,6 +111,7 @@
- [iOS] reduce number of times UIBarButtonItem is recreated ([#41900](https://github.com/expo/expo/pull/41900) by [@Ubax](https://github.com/Ubax))
- [ios] add comment to ENV['RNS_GAMMA_ENABLED'] set by config plugin ([#42231](https://github.com/expo/expo/pull/42231) by [@Ubax](https://github.com/Ubax))
- add `unstable_navigationEvents` to `globalThis.expo` ([#42238](https://github.com/expo/expo/pull/42238) by [@Ubax](https://github.com/Ubax))
+- Upgrade react-native-screens to 4.20.0 ([#42282](https://github.com/expo/expo/pull/42282) by [@Ubax](https://github.com/Ubax))
## 6.0.17 - 2025-12-05
diff --git a/packages/expo-router/build/loaders/utils.d.ts b/packages/expo-router/build/loaders/utils.d.ts
index ab48989a5b342d..ddf75a55abf671 100644
--- a/packages/expo-router/build/loaders/utils.d.ts
+++ b/packages/expo-router/build/loaders/utils.d.ts
@@ -6,13 +6,16 @@
* getLoaderModulePath(`/about`) // `/_expo/loaders/about`
* getLoaderModulePath(`/posts/1`) // `/_expo/loaders/posts/1`
*/
-export declare function getLoaderModulePath(pathname: string): string;
+export declare function getLoaderModulePath(routePath: string): string;
/**
* Fetches and parses a loader module from the given route path.
* This works in all environments including:
- * 1. Development with Metro dev server (see `LoaderModuleMiddleware`)
+ * 1. Development with Metro dev server
* 2. Production with static files (SSG)
* 3. SSR environments
+ *
+ * @see import('packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts').createRouteHandlerMiddleware
+ * @see import('packages/expo-server/src/vendor/environment/common.ts').createEnvironment
*/
export declare function fetchLoaderModule(routePath: string): Promise;
//# sourceMappingURL=utils.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-router/build/loaders/utils.d.ts.map b/packages/expo-router/build/loaders/utils.d.ts.map
index f94aaf04fd53bb..e5106e84bbe043 100644
--- a/packages/expo-router/build/loaders/utils.d.ts.map
+++ b/packages/expo-router/build/loaders/utils.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/loaders/utils.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM5D;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAiBvE"}
\ No newline at end of file
+{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/loaders/utils.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAM7D;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAiBvE"}
\ No newline at end of file
diff --git a/packages/expo-router/build/loaders/utils.js b/packages/expo-router/build/loaders/utils.js
index 3d617234e1fb1c..04b70aa3464c1e 100644
--- a/packages/expo-router/build/loaders/utils.js
+++ b/packages/expo-router/build/loaders/utils.js
@@ -11,18 +11,21 @@ const url_1 = require("../utils/url");
* getLoaderModulePath(`/about`) // `/_expo/loaders/about`
* getLoaderModulePath(`/posts/1`) // `/_expo/loaders/posts/1`
*/
-function getLoaderModulePath(pathname) {
- const urlPath = (0, url_1.parseUrlUsingCustomBase)(pathname).pathname;
- const normalizedPath = urlPath === '/' ? '/' : urlPath.replace(/\/$/, '');
+function getLoaderModulePath(routePath) {
+ const { pathname, search } = (0, url_1.parseUrlUsingCustomBase)(routePath);
+ const normalizedPath = pathname === '/' ? '/' : pathname.replace(/\/$/, '');
const pathSegment = normalizedPath === '/' ? '/index' : normalizedPath;
- return `/_expo/loaders${pathSegment}`;
+ return `/_expo/loaders${pathSegment}${search}`;
}
/**
* Fetches and parses a loader module from the given route path.
* This works in all environments including:
- * 1. Development with Metro dev server (see `LoaderModuleMiddleware`)
+ * 1. Development with Metro dev server
* 2. Production with static files (SSG)
* 3. SSR environments
+ *
+ * @see import('packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts').createRouteHandlerMiddleware
+ * @see import('packages/expo-server/src/vendor/environment/common.ts').createEnvironment
*/
async function fetchLoaderModule(routePath) {
const loaderPath = getLoaderModulePath(routePath);
diff --git a/packages/expo-router/build/loaders/utils.js.map b/packages/expo-router/build/loaders/utils.js.map
index 117e7d7fd90561..72cd349c25c863 100644
--- a/packages/expo-router/build/loaders/utils.js.map
+++ b/packages/expo-router/build/loaders/utils.js.map
@@ -1 +1 @@
-{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/loaders/utils.ts"],"names":[],"mappings":";;AAUA,kDAMC;AASD,8CAiBC;AA1CD,sCAAuD;AAEvD;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,QAAgB;IAClD,MAAM,OAAO,GAAG,IAAA,6BAAuB,EAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC;IAC3D,MAAM,cAAc,GAAG,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1E,MAAM,WAAW,GAAG,cAAc,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC;IAEvE,OAAO,iBAAiB,WAAW,EAAE,CAAC;AACxC,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,iBAAiB,CAAC,SAAiB;IACvD,MAAM,UAAU,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAElD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE;QACvC,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;SAC3B;KACF,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC","sourcesContent":["import { parseUrlUsingCustomBase } from '../utils/url';\n\n/**\n * Convert a route's pathname to a loader module path.\n *\n * @example\n * getLoaderModulePath(`/`); // `/_expo/loaders/index`\n * getLoaderModulePath(`/about`) // `/_expo/loaders/about`\n * getLoaderModulePath(`/posts/1`) // `/_expo/loaders/posts/1`\n */\nexport function getLoaderModulePath(pathname: string): string {\n const urlPath = parseUrlUsingCustomBase(pathname).pathname;\n const normalizedPath = urlPath === '/' ? '/' : urlPath.replace(/\\/$/, '');\n const pathSegment = normalizedPath === '/' ? '/index' : normalizedPath;\n\n return `/_expo/loaders${pathSegment}`;\n}\n\n/**\n * Fetches and parses a loader module from the given route path.\n * This works in all environments including:\n * 1. Development with Metro dev server (see `LoaderModuleMiddleware`)\n * 2. Production with static files (SSG)\n * 3. SSR environments\n */\nexport async function fetchLoaderModule(routePath: string): Promise {\n const loaderPath = getLoaderModulePath(routePath);\n\n const response = await fetch(loaderPath, {\n headers: {\n Accept: 'application/json',\n },\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch loader data: ${response.status}`);\n }\n\n try {\n return await response.json();\n } catch (error) {\n throw new Error(`Failed to parse loader data: ${error}`);\n }\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/loaders/utils.ts"],"names":[],"mappings":";;AAUA,kDAMC;AAYD,8CAiBC;AA7CD,sCAAuD;AAEvD;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,SAAiB;IACnD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAA,6BAAuB,EAAC,SAAS,CAAC,CAAC;IAChE,MAAM,cAAc,GAAG,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC5E,MAAM,WAAW,GAAG,cAAc,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC;IAEvE,OAAO,iBAAiB,WAAW,GAAG,MAAM,EAAE,CAAC;AACjD,CAAC;AAED;;;;;;;;;GASG;AACI,KAAK,UAAU,iBAAiB,CAAC,SAAiB;IACvD,MAAM,UAAU,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAElD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE;QACvC,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;SAC3B;KACF,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC","sourcesContent":["import { parseUrlUsingCustomBase } from '../utils/url';\n\n/**\n * Convert a route's pathname to a loader module path.\n *\n * @example\n * getLoaderModulePath(`/`); // `/_expo/loaders/index`\n * getLoaderModulePath(`/about`) // `/_expo/loaders/about`\n * getLoaderModulePath(`/posts/1`) // `/_expo/loaders/posts/1`\n */\nexport function getLoaderModulePath(routePath: string): string {\n const { pathname, search } = parseUrlUsingCustomBase(routePath);\n const normalizedPath = pathname === '/' ? '/' : pathname.replace(/\\/$/, '');\n const pathSegment = normalizedPath === '/' ? '/index' : normalizedPath;\n\n return `/_expo/loaders${pathSegment}${search}`;\n}\n\n/**\n * Fetches and parses a loader module from the given route path.\n * This works in all environments including:\n * 1. Development with Metro dev server\n * 2. Production with static files (SSG)\n * 3. SSR environments\n *\n * @see import('packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts').createRouteHandlerMiddleware\n * @see import('packages/expo-server/src/vendor/environment/common.ts').createEnvironment\n */\nexport async function fetchLoaderModule(routePath: string): Promise {\n const loaderPath = getLoaderModulePath(routePath);\n\n const response = await fetch(loaderPath, {\n headers: {\n Accept: 'application/json',\n },\n });\n if (!response.ok) {\n throw new Error(`Failed to fetch loader data: ${response.status}`);\n }\n\n try {\n return await response.json();\n } catch (error) {\n throw new Error(`Failed to parse loader data: ${error}`);\n }\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/NativeTabsView.d.ts.map b/packages/expo-router/build/native-tabs/NativeTabsView.d.ts.map
index 674c484b2fb2a5..d5aa175225f02c 100644
--- a/packages/expo-router/build/native-tabs/NativeTabsView.d.ts.map
+++ b/packages/expo-router/build/native-tabs/NativeTabsView.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"NativeTabsView.d.ts","sourceRoot":"","sources":["../../src/native-tabs/NativeTabsView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAoC,MAAM,OAAO,CAAC;AAgBzD,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,SAAS,CAAC;AASjB,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,qBAyHxD"}
\ No newline at end of file
+{"version":3,"file":"NativeTabsView.d.ts","sourceRoot":"","sources":["../../src/native-tabs/NativeTabsView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAoC,MAAM,OAAO,CAAC;AAWzD,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,SAAS,CAAC;AASjB,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,qBAyHxD"}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/NativeTabsView.js b/packages/expo-router/build/native-tabs/NativeTabsView.js
index 4fa56cf5ac8342..188c867f13d21a 100644
--- a/packages/expo-router/build/native-tabs/NativeTabsView.js
+++ b/packages/expo-router/build/native-tabs/NativeTabsView.js
@@ -82,7 +82,7 @@ function NativeTabsView(props) {
indicatorColor: color_1.Color.android.dynamic.secondaryContainer,
}
: undefined;
- return (
{children}
- );
+ );
}
function Screen(props) {
const { routeKey, name, options, isFocused, standardAppearance, scrollEdgeAppearance, badgeTextColor, contentRenderer, } = props;
@@ -125,13 +125,13 @@ function Screen(props) {
collapsable={false} style={{ flex: 1 }} edges={{ bottom: true }}>
{content}
) : (content);
- return (
+ return (
{wrappedContent}
- );
+ );
}
const supportedTabBarMinimizeBehaviorsSet = new Set(types_1.SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS);
const supportedTabBarItemLabelVisibilityModesSet = new Set(types_1.SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES);
-function BottomTabsWrapper(props) {
+function TabsHostWrapper(props) {
let { tabBarMinimizeBehavior, tabBarItemLabelVisibilityMode, ...rest } = props;
if (tabBarMinimizeBehavior && !supportedTabBarMinimizeBehaviorsSet.has(tabBarMinimizeBehavior)) {
console.warn(`Unsupported minimizeBehavior: ${tabBarMinimizeBehavior}. Supported values are: ${types_1.SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS.map((behavior) => `"${behavior}"`).join(', ')}`);
@@ -142,6 +142,6 @@ function BottomTabsWrapper(props) {
console.warn(`Unsupported labelVisibilityMode: ${tabBarItemLabelVisibilityMode}. Supported values are: ${types_1.SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES.map((mode) => `"${mode}"`).join(', ')}`);
tabBarItemLabelVisibilityMode = undefined;
}
- return ();
+ return ();
}
//# sourceMappingURL=NativeTabsView.js.map
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/NativeTabsView.js.map b/packages/expo-router/build/native-tabs/NativeTabsView.js.map
index b2f85b979488c6..32e5ae8c409d7c 100644
--- a/packages/expo-router/build/native-tabs/NativeTabsView.js.map
+++ b/packages/expo-router/build/native-tabs/NativeTabsView.js.map
@@ -1 +1 @@
-{"version":3,"file":"NativeTabsView.js","sourceRoot":"","sources":["../../src/native-tabs/NativeTabsView.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useTheme } from '@react-navigation/native';\nimport React, { useDeferredValue, useMemo } from 'react';\nimport { View, type ColorValue } from 'react-native';\nimport {\n BottomTabs,\n BottomTabsScreen,\n type BottomTabsProps,\n type BottomTabsScreenAppearance,\n} from 'react-native-screens';\nimport { SafeAreaView } from 'react-native-screens/experimental';\n\nimport {\n createScrollEdgeAppearanceFromOptions,\n createStandardAppearanceFromOptions,\n} from './appearance';\nimport { Color } from '../color';\nimport { NativeTabsBottomAccessory } from './common/elements';\nimport {\n SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES,\n SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS,\n type NativeTabOptions,\n type NativeTabsViewProps,\n} from './types';\nimport {\n convertOptionsIconToRNScreensPropsIcon,\n convertOptionsIconToIOSPropsIcon,\n useAwaitedScreensIcon,\n} from './utils/icon';\nimport { getFirstChildOfType } from '../utils/children';\nimport { useBottomAccessoryFunctionFromBottomAccessories } from './utils/bottomAccessory';\n\nexport function NativeTabsView(props: NativeTabsViewProps) {\n const {\n minimizeBehavior,\n disableIndicator,\n focusedIndex,\n tabs,\n sidebarAdaptable,\n nonTriggerChildren,\n } = props;\n\n const deferredFocusedIndex = useDeferredValue(focusedIndex);\n // We need to check if the deferred index is not out of bounds\n // This can happen when the focused index is the last tab, and user removes that tab\n // In that case the deferred index will still point to the last tab, but after re-render\n // it will be out of bounds\n const inBoundsDeferredFocusedIndex =\n deferredFocusedIndex < tabs.length ? deferredFocusedIndex : focusedIndex;\n\n const appearances = tabs.map((tab) => ({\n standardAppearance: createStandardAppearanceFromOptions(tab.options),\n scrollEdgeAppearance: createScrollEdgeAppearanceFromOptions(tab.options),\n }));\n\n const options = tabs.map((tab) => tab.options);\n\n const bottomAccessory = useMemo(\n () => getFirstChildOfType(nonTriggerChildren, NativeTabsBottomAccessory),\n [nonTriggerChildren]\n );\n\n const bottomAccessoryFn = useBottomAccessoryFunctionFromBottomAccessories(bottomAccessory);\n\n const children = tabs.map((tab, index) => {\n const isFocused = index === inBoundsDeferredFocusedIndex;\n\n return (\n \n );\n });\n\n const currentTabAppearance = appearances[inBoundsDeferredFocusedIndex]?.standardAppearance;\n const tabBarControllerMode: BottomTabsProps['tabBarControllerMode'] = sidebarAdaptable\n ? 'tabSidebar'\n : sidebarAdaptable === false\n ? 'tabBar'\n : 'automatic';\n\n // Material Design 3 dynamic color defaults for Android\n const androidMaterialDefaults =\n process.env.EXPO_OS === 'android'\n ? {\n inactiveColor: Color.android.dynamic.onSurfaceVariant,\n activeIconColor: Color.android.dynamic.onSecondaryContainer,\n activeLabelColor: Color.android.dynamic.onSurface,\n backgroundColor: Color.android.dynamic.surfaceContainer,\n rippleColor: Color.android.dynamic.primary,\n indicatorColor: Color.android.dynamic.secondaryContainer,\n }\n : undefined;\n\n return (\n {\n props.onTabChange(tabKey);\n }}>\n {children}\n \n );\n}\n\nfunction Screen(props: {\n routeKey: string;\n name: string;\n isFocused: boolean;\n options: NativeTabOptions;\n standardAppearance: BottomTabsScreenAppearance;\n scrollEdgeAppearance: BottomTabsScreenAppearance;\n badgeTextColor: ColorValue | undefined;\n contentRenderer: () => React.ReactNode;\n}) {\n const {\n routeKey,\n name,\n options,\n isFocused,\n standardAppearance,\n scrollEdgeAppearance,\n badgeTextColor,\n contentRenderer,\n } = props;\n const title = options.title ?? name;\n\n // We need to await the icon, as VectorIcon will load asynchronously\n const icon = useAwaitedScreensIcon(options.icon);\n const selectedIcon = useAwaitedScreensIcon(options.selectedIcon);\n const { colors } = useTheme();\n\n const content = (\n \n {contentRenderer()}\n \n );\n const wrappedContent =\n process.env.EXPO_OS === 'android' && !options.disableAutomaticContentInsets ? (\n \n {content}\n \n ) : (\n content\n );\n\n return (\n \n {wrappedContent}\n \n );\n}\n\nconst supportedTabBarMinimizeBehaviorsSet = new Set(SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS);\nconst supportedTabBarItemLabelVisibilityModesSet = new Set(\n SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES\n);\n\nfunction BottomTabsWrapper(props: BottomTabsProps) {\n let { tabBarMinimizeBehavior, tabBarItemLabelVisibilityMode, ...rest } = props;\n if (tabBarMinimizeBehavior && !supportedTabBarMinimizeBehaviorsSet.has(tabBarMinimizeBehavior)) {\n console.warn(\n `Unsupported minimizeBehavior: ${tabBarMinimizeBehavior}. Supported values are: ${SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS.map((behavior) => `\"${behavior}\"`).join(', ')}`\n );\n tabBarMinimizeBehavior = undefined;\n }\n if (\n tabBarItemLabelVisibilityMode &&\n !supportedTabBarItemLabelVisibilityModesSet.has(tabBarItemLabelVisibilityMode)\n ) {\n console.warn(\n `Unsupported labelVisibilityMode: ${tabBarItemLabelVisibilityMode}. Supported values are: ${SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES.map((mode) => `\"${mode}\"`).join(', ')}`\n );\n tabBarItemLabelVisibilityMode = undefined;\n }\n\n return (\n \n );\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"NativeTabsView.js","sourceRoot":"","sources":["../../src/native-tabs/NativeTabsView.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useTheme } from '@react-navigation/native';\nimport React, { useDeferredValue, useMemo } from 'react';\nimport { View, type ColorValue } from 'react-native';\nimport { Tabs, type TabsHostProps, type TabsScreenAppearance } from 'react-native-screens';\nimport { SafeAreaView } from 'react-native-screens/experimental';\n\nimport {\n createScrollEdgeAppearanceFromOptions,\n createStandardAppearanceFromOptions,\n} from './appearance';\nimport { Color } from '../color';\nimport { NativeTabsBottomAccessory } from './common/elements';\nimport {\n SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES,\n SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS,\n type NativeTabOptions,\n type NativeTabsViewProps,\n} from './types';\nimport {\n convertOptionsIconToRNScreensPropsIcon,\n convertOptionsIconToIOSPropsIcon,\n useAwaitedScreensIcon,\n} from './utils/icon';\nimport { getFirstChildOfType } from '../utils/children';\nimport { useBottomAccessoryFunctionFromBottomAccessories } from './utils/bottomAccessory';\n\nexport function NativeTabsView(props: NativeTabsViewProps) {\n const {\n minimizeBehavior,\n disableIndicator,\n focusedIndex,\n tabs,\n sidebarAdaptable,\n nonTriggerChildren,\n } = props;\n\n const deferredFocusedIndex = useDeferredValue(focusedIndex);\n // We need to check if the deferred index is not out of bounds\n // This can happen when the focused index is the last tab, and user removes that tab\n // In that case the deferred index will still point to the last tab, but after re-render\n // it will be out of bounds\n const inBoundsDeferredFocusedIndex =\n deferredFocusedIndex < tabs.length ? deferredFocusedIndex : focusedIndex;\n\n const appearances = tabs.map((tab) => ({\n standardAppearance: createStandardAppearanceFromOptions(tab.options),\n scrollEdgeAppearance: createScrollEdgeAppearanceFromOptions(tab.options),\n }));\n\n const options = tabs.map((tab) => tab.options);\n\n const bottomAccessory = useMemo(\n () => getFirstChildOfType(nonTriggerChildren, NativeTabsBottomAccessory),\n [nonTriggerChildren]\n );\n\n const bottomAccessoryFn = useBottomAccessoryFunctionFromBottomAccessories(bottomAccessory);\n\n const children = tabs.map((tab, index) => {\n const isFocused = index === inBoundsDeferredFocusedIndex;\n\n return (\n \n );\n });\n\n const currentTabAppearance = appearances[inBoundsDeferredFocusedIndex]?.standardAppearance;\n const tabBarControllerMode: TabsHostProps['tabBarControllerMode'] = sidebarAdaptable\n ? 'tabSidebar'\n : sidebarAdaptable === false\n ? 'tabBar'\n : 'automatic';\n\n // Material Design 3 dynamic color defaults for Android\n const androidMaterialDefaults =\n process.env.EXPO_OS === 'android'\n ? {\n inactiveColor: Color.android.dynamic.onSurfaceVariant,\n activeIconColor: Color.android.dynamic.onSecondaryContainer,\n activeLabelColor: Color.android.dynamic.onSurface,\n backgroundColor: Color.android.dynamic.surfaceContainer,\n rippleColor: Color.android.dynamic.primary,\n indicatorColor: Color.android.dynamic.secondaryContainer,\n }\n : undefined;\n\n return (\n {\n props.onTabChange(tabKey);\n }}>\n {children}\n \n );\n}\n\nfunction Screen(props: {\n routeKey: string;\n name: string;\n isFocused: boolean;\n options: NativeTabOptions;\n standardAppearance: TabsScreenAppearance;\n scrollEdgeAppearance: TabsScreenAppearance;\n badgeTextColor: ColorValue | undefined;\n contentRenderer: () => React.ReactNode;\n}) {\n const {\n routeKey,\n name,\n options,\n isFocused,\n standardAppearance,\n scrollEdgeAppearance,\n badgeTextColor,\n contentRenderer,\n } = props;\n const title = options.title ?? name;\n\n // We need to await the icon, as VectorIcon will load asynchronously\n const icon = useAwaitedScreensIcon(options.icon);\n const selectedIcon = useAwaitedScreensIcon(options.selectedIcon);\n const { colors } = useTheme();\n\n const content = (\n \n {contentRenderer()}\n \n );\n const wrappedContent =\n process.env.EXPO_OS === 'android' && !options.disableAutomaticContentInsets ? (\n \n {content}\n \n ) : (\n content\n );\n\n return (\n \n {wrappedContent}\n \n );\n}\n\nconst supportedTabBarMinimizeBehaviorsSet = new Set(SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS);\nconst supportedTabBarItemLabelVisibilityModesSet = new Set(\n SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES\n);\n\nfunction TabsHostWrapper(props: TabsHostProps) {\n let { tabBarMinimizeBehavior, tabBarItemLabelVisibilityMode, ...rest } = props;\n if (tabBarMinimizeBehavior && !supportedTabBarMinimizeBehaviorsSet.has(tabBarMinimizeBehavior)) {\n console.warn(\n `Unsupported minimizeBehavior: ${tabBarMinimizeBehavior}. Supported values are: ${SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS.map((behavior) => `\"${behavior}\"`).join(', ')}`\n );\n tabBarMinimizeBehavior = undefined;\n }\n if (\n tabBarItemLabelVisibilityMode &&\n !supportedTabBarItemLabelVisibilityModesSet.has(tabBarItemLabelVisibilityMode)\n ) {\n console.warn(\n `Unsupported labelVisibilityMode: ${tabBarItemLabelVisibilityMode}. Supported values are: ${SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES.map((mode) => `\"${mode}\"`).join(', ')}`\n );\n tabBarItemLabelVisibilityMode = undefined;\n }\n\n return (\n \n );\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/appearance.d.ts b/packages/expo-router/build/native-tabs/appearance.d.ts
index 8c05405e740136..6cf4b0f76dd47d 100644
--- a/packages/expo-router/build/native-tabs/appearance.d.ts
+++ b/packages/expo-router/build/native-tabs/appearance.d.ts
@@ -1,8 +1,8 @@
import type { ColorValue } from 'react-native';
-import type { BottomTabsScreenAppearance, BottomTabsScreenItemStateAppearance } from 'react-native-screens';
+import type { TabsScreenAppearance, TabsScreenItemStateAppearance } from 'react-native-screens';
import { type NativeTabOptions, type NativeTabsBlurEffect, type NativeTabsLabelStyle } from './types';
-export declare function createStandardAppearanceFromOptions(options: NativeTabOptions): BottomTabsScreenAppearance;
-export declare function createScrollEdgeAppearanceFromOptions(options: NativeTabOptions): BottomTabsScreenAppearance;
+export declare function createStandardAppearanceFromOptions(options: NativeTabOptions): TabsScreenAppearance;
+export declare function createScrollEdgeAppearanceFromOptions(options: NativeTabOptions): TabsScreenAppearance;
export interface AppearanceStyle extends NativeTabsLabelStyle {
iconColor?: ColorValue;
backgroundColor?: ColorValue | null;
@@ -14,8 +14,8 @@ export interface AppearanceStyle extends NativeTabsLabelStyle {
vertical?: number;
};
}
-export declare function appendSelectedStyleToAppearance(selectedStyle: AppearanceStyle, appearance: BottomTabsScreenAppearance): BottomTabsScreenAppearance;
-export declare function appendStyleToAppearance(style: AppearanceStyle, appearance: BottomTabsScreenAppearance, states: ('selected' | 'focused' | 'disabled' | 'normal')[]): BottomTabsScreenAppearance;
-export declare function convertStyleToAppearance(style: AppearanceStyle | undefined): BottomTabsScreenAppearance;
-export declare function convertStyleToItemStateAppearance(style: AppearanceStyle | undefined): BottomTabsScreenItemStateAppearance;
+export declare function appendSelectedStyleToAppearance(selectedStyle: AppearanceStyle, appearance: TabsScreenAppearance): TabsScreenAppearance;
+export declare function appendStyleToAppearance(style: AppearanceStyle, appearance: TabsScreenAppearance, states: ('selected' | 'focused' | 'disabled' | 'normal')[]): TabsScreenAppearance;
+export declare function convertStyleToAppearance(style: AppearanceStyle | undefined): TabsScreenAppearance;
+export declare function convertStyleToItemStateAppearance(style: AppearanceStyle | undefined): TabsScreenItemStateAppearance;
//# sourceMappingURL=appearance.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/appearance.d.ts.map b/packages/expo-router/build/native-tabs/appearance.d.ts.map
index c137576c37e784..74b224e727d4c3 100644
--- a/packages/expo-router/build/native-tabs/appearance.d.ts.map
+++ b/packages/expo-router/build/native-tabs/appearance.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../../src/native-tabs/appearance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EACV,0BAA0B,EAE1B,mCAAmC,EACpC,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAC1B,MAAM,SAAS,CAAC;AAKjB,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,gBAAgB,GACxB,0BAA0B,CAgC5B;AAED,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,gBAAgB,GACxB,0BAA0B,CAgC5B;AAED,MAAM,WAAW,eAAgB,SAAQ,oBAAoB;IAC3D,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,eAAe,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,oBAAoB,CAAC,EAAE,UAAU,CAAC;IAClC,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,uBAAuB,CAAC,EAAE;QACxB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,wBAAgB,+BAA+B,CAC7C,aAAa,EAAE,eAAe,EAC9B,UAAU,EAAE,0BAA0B,GACrC,0BAA0B,CAE5B;AASD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,0BAA0B,EACtC,MAAM,EAAE,CAAC,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC,EAAE,GACzD,0BAA0B,CA8B5B;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,eAAe,GAAG,SAAS,GACjC,0BAA0B,CAmB5B;AAED,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,eAAe,GAAG,SAAS,GACjC,mCAAmC,CAsBrC"}
\ No newline at end of file
+{"version":3,"file":"appearance.d.ts","sourceRoot":"","sources":["../../src/native-tabs/appearance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EACV,oBAAoB,EAEpB,6BAA6B,EAC9B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAC1B,MAAM,SAAS,CAAC;AAKjB,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,gBAAgB,GACxB,oBAAoB,CAgCtB;AAED,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,gBAAgB,GACxB,oBAAoB,CAgCtB;AAED,MAAM,WAAW,eAAgB,SAAQ,oBAAoB;IAC3D,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,eAAe,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,oBAAoB,CAAC,EAAE,UAAU,CAAC;IAClC,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,uBAAuB,CAAC,EAAE;QACxB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,wBAAgB,+BAA+B,CAC7C,aAAa,EAAE,eAAe,EAC9B,UAAU,EAAE,oBAAoB,GAC/B,oBAAoB,CAEtB;AASD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,CAAC,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC,EAAE,GACzD,oBAAoB,CA8BtB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,oBAAoB,CAmBjG;AAED,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,eAAe,GAAG,SAAS,GACjC,6BAA6B,CAsB/B"}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/appearance.js.map b/packages/expo-router/build/native-tabs/appearance.js.map
index 9e5c9bf4fc4f8c..b08a481add7285 100644
--- a/packages/expo-router/build/native-tabs/appearance.js.map
+++ b/packages/expo-router/build/native-tabs/appearance.js.map
@@ -1 +1 @@
-{"version":3,"file":"appearance.js","sourceRoot":"","sources":["../../src/native-tabs/appearance.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorValue } from 'react-native';\nimport type {\n BottomTabsScreenAppearance,\n BottomTabsScreenItemAppearance,\n BottomTabsScreenItemStateAppearance,\n} from 'react-native-screens';\n\nimport {\n SUPPORTED_BLUR_EFFECTS,\n type NativeTabOptions,\n type NativeTabsBlurEffect,\n type NativeTabsLabelStyle,\n} from './types';\nimport { convertFontWeightToStringFontWeight } from '../utils/style';\n\nconst supportedBlurEffectsSet = new Set(SUPPORTED_BLUR_EFFECTS);\n\nexport function createStandardAppearanceFromOptions(\n options: NativeTabOptions\n): BottomTabsScreenAppearance {\n let blurEffect = options.blurEffect;\n if (blurEffect && !supportedBlurEffectsSet.has(blurEffect)) {\n console.warn(\n `Unsupported blurEffect: ${blurEffect}. Supported values are: ${SUPPORTED_BLUR_EFFECTS.map(\n (effect) => `\"${effect}\"`\n ).join(', ')}`\n );\n blurEffect = undefined;\n }\n const appearance = appendStyleToAppearance(\n {\n ...options.labelStyle,\n iconColor: options.iconColor,\n backgroundColor: options.backgroundColor,\n blurEffect,\n badgeBackgroundColor: options.badgeBackgroundColor,\n titlePositionAdjustment: options.titlePositionAdjustment,\n shadowColor: options.shadowColor,\n },\n {},\n ['normal', 'focused', 'selected']\n );\n return appendSelectedStyleToAppearance(\n {\n ...(options.selectedLabelStyle ?? {}),\n iconColor: options.selectedIconColor,\n badgeBackgroundColor: options.selectedBadgeBackgroundColor,\n titlePositionAdjustment: options.selectedTitlePositionAdjustment,\n },\n appearance\n );\n}\n\nexport function createScrollEdgeAppearanceFromOptions(\n options: NativeTabOptions\n): BottomTabsScreenAppearance {\n let blurEffect = options.disableTransparentOnScrollEdge ? options.blurEffect : 'none';\n if (blurEffect && !supportedBlurEffectsSet.has(blurEffect)) {\n console.warn(\n `Unsupported blurEffect: ${blurEffect}. Supported values are: ${SUPPORTED_BLUR_EFFECTS.map(\n (effect) => `\"${effect}\"`\n ).join(', ')}`\n );\n blurEffect = undefined;\n }\n const appearance = appendStyleToAppearance(\n {\n ...options.labelStyle,\n iconColor: options.iconColor,\n blurEffect,\n backgroundColor: options.disableTransparentOnScrollEdge ? options.backgroundColor : null,\n shadowColor: options.disableTransparentOnScrollEdge ? options.shadowColor : 'transparent',\n badgeBackgroundColor: options.badgeBackgroundColor,\n titlePositionAdjustment: options.titlePositionAdjustment,\n },\n {},\n ['normal', 'focused', 'selected']\n );\n return appendSelectedStyleToAppearance(\n {\n ...(options.selectedLabelStyle ?? {}),\n iconColor: options.selectedIconColor,\n badgeBackgroundColor: options.selectedBadgeBackgroundColor,\n titlePositionAdjustment: options.selectedTitlePositionAdjustment,\n },\n appearance\n );\n}\n\nexport interface AppearanceStyle extends NativeTabsLabelStyle {\n iconColor?: ColorValue;\n backgroundColor?: ColorValue | null;\n blurEffect?: NativeTabsBlurEffect;\n badgeBackgroundColor?: ColorValue;\n shadowColor?: ColorValue;\n titlePositionAdjustment?: {\n horizontal?: number;\n vertical?: number;\n };\n}\n\nexport function appendSelectedStyleToAppearance(\n selectedStyle: AppearanceStyle,\n appearance: BottomTabsScreenAppearance\n): BottomTabsScreenAppearance {\n return appendStyleToAppearance(selectedStyle, appearance, ['selected', 'focused']);\n}\n\nconst EMPTY_APPEARANCE_ITEM: BottomTabsScreenItemAppearance = {\n normal: {},\n selected: {},\n focused: {},\n disabled: {},\n};\n\nexport function appendStyleToAppearance(\n style: AppearanceStyle,\n appearance: BottomTabsScreenAppearance,\n states: ('selected' | 'focused' | 'disabled' | 'normal')[]\n): BottomTabsScreenAppearance {\n const baseItemAppearance =\n appearance.stacked || appearance.inline || appearance.compactInline || {};\n\n const styleAppearance = convertStyleToAppearance(style);\n const newAppearances = states.map((state) => ({\n key: state,\n appearance: {\n ...baseItemAppearance.normal,\n ...baseItemAppearance[state],\n ...styleAppearance.stacked?.normal,\n },\n }));\n\n const itemAppearance: BottomTabsScreenItemAppearance = {\n ...EMPTY_APPEARANCE_ITEM,\n ...baseItemAppearance,\n ...Object.fromEntries(newAppearances.map(({ key, appearance }) => [key, appearance])),\n };\n return {\n stacked: itemAppearance,\n inline: itemAppearance,\n compactInline: itemAppearance,\n tabBarBackgroundColor:\n style.backgroundColor === null\n ? undefined\n : (style.backgroundColor ?? appearance.tabBarBackgroundColor),\n tabBarBlurEffect: styleAppearance.tabBarBlurEffect ?? appearance.tabBarBlurEffect,\n tabBarShadowColor: styleAppearance.tabBarShadowColor ?? appearance.tabBarShadowColor,\n };\n}\n\nexport function convertStyleToAppearance(\n style: AppearanceStyle | undefined\n): BottomTabsScreenAppearance {\n if (!style) {\n return {};\n }\n const stateAppearance = convertStyleToItemStateAppearance(style);\n const itemAppearance: BottomTabsScreenItemAppearance = {\n normal: stateAppearance,\n selected: stateAppearance,\n focused: stateAppearance,\n disabled: {},\n };\n return {\n inline: itemAppearance,\n stacked: itemAppearance,\n compactInline: itemAppearance,\n tabBarBackgroundColor: style?.backgroundColor ?? undefined,\n tabBarBlurEffect: style?.blurEffect,\n tabBarShadowColor: style?.shadowColor,\n };\n}\n\nexport function convertStyleToItemStateAppearance(\n style: AppearanceStyle | undefined\n): BottomTabsScreenItemStateAppearance {\n if (!style) {\n return {};\n }\n const stateAppearance: BottomTabsScreenItemStateAppearance = {\n tabBarItemBadgeBackgroundColor: style.badgeBackgroundColor,\n tabBarItemTitlePositionAdjustment: style.titlePositionAdjustment,\n tabBarItemIconColor: style.iconColor,\n tabBarItemTitleFontFamily: style.fontFamily,\n tabBarItemTitleFontSize: style.fontSize,\n tabBarItemTitleFontWeight: convertFontWeightToStringFontWeight(style.fontWeight),\n tabBarItemTitleFontStyle: style.fontStyle,\n tabBarItemTitleFontColor: style.color,\n };\n\n (Object.keys(stateAppearance) as (keyof BottomTabsScreenItemStateAppearance)[]).forEach((key) => {\n if (stateAppearance[key] === undefined) {\n delete stateAppearance[key];\n }\n });\n\n return stateAppearance;\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"appearance.js","sourceRoot":"","sources":["../../src/native-tabs/appearance.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorValue } from 'react-native';\nimport type {\n TabsScreenAppearance,\n TabsScreenItemAppearance,\n TabsScreenItemStateAppearance,\n} from 'react-native-screens';\n\nimport {\n SUPPORTED_BLUR_EFFECTS,\n type NativeTabOptions,\n type NativeTabsBlurEffect,\n type NativeTabsLabelStyle,\n} from './types';\nimport { convertFontWeightToStringFontWeight } from '../utils/style';\n\nconst supportedBlurEffectsSet = new Set(SUPPORTED_BLUR_EFFECTS);\n\nexport function createStandardAppearanceFromOptions(\n options: NativeTabOptions\n): TabsScreenAppearance {\n let blurEffect = options.blurEffect;\n if (blurEffect && !supportedBlurEffectsSet.has(blurEffect)) {\n console.warn(\n `Unsupported blurEffect: ${blurEffect}. Supported values are: ${SUPPORTED_BLUR_EFFECTS.map(\n (effect) => `\"${effect}\"`\n ).join(', ')}`\n );\n blurEffect = undefined;\n }\n const appearance = appendStyleToAppearance(\n {\n ...options.labelStyle,\n iconColor: options.iconColor,\n backgroundColor: options.backgroundColor,\n blurEffect,\n badgeBackgroundColor: options.badgeBackgroundColor,\n titlePositionAdjustment: options.titlePositionAdjustment,\n shadowColor: options.shadowColor,\n },\n {},\n ['normal', 'focused', 'selected']\n );\n return appendSelectedStyleToAppearance(\n {\n ...(options.selectedLabelStyle ?? {}),\n iconColor: options.selectedIconColor,\n badgeBackgroundColor: options.selectedBadgeBackgroundColor,\n titlePositionAdjustment: options.selectedTitlePositionAdjustment,\n },\n appearance\n );\n}\n\nexport function createScrollEdgeAppearanceFromOptions(\n options: NativeTabOptions\n): TabsScreenAppearance {\n let blurEffect = options.disableTransparentOnScrollEdge ? options.blurEffect : 'none';\n if (blurEffect && !supportedBlurEffectsSet.has(blurEffect)) {\n console.warn(\n `Unsupported blurEffect: ${blurEffect}. Supported values are: ${SUPPORTED_BLUR_EFFECTS.map(\n (effect) => `\"${effect}\"`\n ).join(', ')}`\n );\n blurEffect = undefined;\n }\n const appearance = appendStyleToAppearance(\n {\n ...options.labelStyle,\n iconColor: options.iconColor,\n blurEffect,\n backgroundColor: options.disableTransparentOnScrollEdge ? options.backgroundColor : null,\n shadowColor: options.disableTransparentOnScrollEdge ? options.shadowColor : 'transparent',\n badgeBackgroundColor: options.badgeBackgroundColor,\n titlePositionAdjustment: options.titlePositionAdjustment,\n },\n {},\n ['normal', 'focused', 'selected']\n );\n return appendSelectedStyleToAppearance(\n {\n ...(options.selectedLabelStyle ?? {}),\n iconColor: options.selectedIconColor,\n badgeBackgroundColor: options.selectedBadgeBackgroundColor,\n titlePositionAdjustment: options.selectedTitlePositionAdjustment,\n },\n appearance\n );\n}\n\nexport interface AppearanceStyle extends NativeTabsLabelStyle {\n iconColor?: ColorValue;\n backgroundColor?: ColorValue | null;\n blurEffect?: NativeTabsBlurEffect;\n badgeBackgroundColor?: ColorValue;\n shadowColor?: ColorValue;\n titlePositionAdjustment?: {\n horizontal?: number;\n vertical?: number;\n };\n}\n\nexport function appendSelectedStyleToAppearance(\n selectedStyle: AppearanceStyle,\n appearance: TabsScreenAppearance\n): TabsScreenAppearance {\n return appendStyleToAppearance(selectedStyle, appearance, ['selected', 'focused']);\n}\n\nconst EMPTY_APPEARANCE_ITEM: TabsScreenItemAppearance = {\n normal: {},\n selected: {},\n focused: {},\n disabled: {},\n};\n\nexport function appendStyleToAppearance(\n style: AppearanceStyle,\n appearance: TabsScreenAppearance,\n states: ('selected' | 'focused' | 'disabled' | 'normal')[]\n): TabsScreenAppearance {\n const baseItemAppearance =\n appearance.stacked || appearance.inline || appearance.compactInline || {};\n\n const styleAppearance = convertStyleToAppearance(style);\n const newAppearances = states.map((state) => ({\n key: state,\n appearance: {\n ...baseItemAppearance.normal,\n ...baseItemAppearance[state],\n ...styleAppearance.stacked?.normal,\n },\n }));\n\n const itemAppearance: TabsScreenItemAppearance = {\n ...EMPTY_APPEARANCE_ITEM,\n ...baseItemAppearance,\n ...Object.fromEntries(newAppearances.map(({ key, appearance }) => [key, appearance])),\n };\n return {\n stacked: itemAppearance,\n inline: itemAppearance,\n compactInline: itemAppearance,\n tabBarBackgroundColor:\n style.backgroundColor === null\n ? undefined\n : (style.backgroundColor ?? appearance.tabBarBackgroundColor),\n tabBarBlurEffect: styleAppearance.tabBarBlurEffect ?? appearance.tabBarBlurEffect,\n tabBarShadowColor: styleAppearance.tabBarShadowColor ?? appearance.tabBarShadowColor,\n };\n}\n\nexport function convertStyleToAppearance(style: AppearanceStyle | undefined): TabsScreenAppearance {\n if (!style) {\n return {};\n }\n const stateAppearance = convertStyleToItemStateAppearance(style);\n const itemAppearance: TabsScreenItemAppearance = {\n normal: stateAppearance,\n selected: stateAppearance,\n focused: stateAppearance,\n disabled: {},\n };\n return {\n inline: itemAppearance,\n stacked: itemAppearance,\n compactInline: itemAppearance,\n tabBarBackgroundColor: style?.backgroundColor ?? undefined,\n tabBarBlurEffect: style?.blurEffect,\n tabBarShadowColor: style?.shadowColor,\n };\n}\n\nexport function convertStyleToItemStateAppearance(\n style: AppearanceStyle | undefined\n): TabsScreenItemStateAppearance {\n if (!style) {\n return {};\n }\n const stateAppearance: TabsScreenItemStateAppearance = {\n tabBarItemBadgeBackgroundColor: style.badgeBackgroundColor,\n tabBarItemTitlePositionAdjustment: style.titlePositionAdjustment,\n tabBarItemIconColor: style.iconColor,\n tabBarItemTitleFontFamily: style.fontFamily,\n tabBarItemTitleFontSize: style.fontSize,\n tabBarItemTitleFontWeight: convertFontWeightToStringFontWeight(style.fontWeight),\n tabBarItemTitleFontStyle: style.fontStyle,\n tabBarItemTitleFontColor: style.color,\n };\n\n (Object.keys(stateAppearance) as (keyof TabsScreenItemStateAppearance)[]).forEach((key) => {\n if (stateAppearance[key] === undefined) {\n delete stateAppearance[key];\n }\n });\n\n return stateAppearance;\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/types.d.ts b/packages/expo-router/build/native-tabs/types.d.ts
index 90172c2fe6f0ca..5d364b9836dce8 100644
--- a/packages/expo-router/build/native-tabs/types.d.ts
+++ b/packages/expo-router/build/native-tabs/types.d.ts
@@ -1,9 +1,9 @@
import type { DefaultRouterOptions } from '@react-navigation/native';
import type { PropsWithChildren } from 'react';
import type { ColorValue, ImageSourcePropType, StyleProp, TextStyle, ViewStyle } from 'react-native';
-import type { BottomTabsScreenProps } from 'react-native-screens';
+import type { TabsScreenProps } from 'react-native-screens';
import type { SFSymbol } from 'sf-symbols-typescript';
-export type NativeScreenProps = Partial>;
+export type NativeScreenProps = Partial>;
export interface NativeTabOptions extends DefaultRouterOptions {
icon?: SymbolOrImageSource;
selectedIcon?: SymbolOrImageSource;
@@ -31,7 +31,7 @@ export interface NativeTabOptions extends DefaultRouterOptions {
};
indicatorColor?: ColorValue;
hidden?: boolean;
- specialEffects?: BottomTabsScreenProps['specialEffects'];
+ specialEffects?: TabsScreenProps['specialEffects'];
nativeProps?: NativeScreenProps;
disableAutomaticContentInsets?: boolean;
contentStyle?: Pick;
diff --git a/packages/expo-router/build/native-tabs/types.d.ts.map b/packages/expo-router/build/native-tabs/types.d.ts.map
index f61a7a04301267..d99ccef039f50e 100644
--- a/packages/expo-router/build/native-tabs/types.d.ts.map
+++ b/packages/expo-router/build/native-tabs/types.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/native-tabs/types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/native-tabs/types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/types.js.map b/packages/expo-router/build/native-tabs/types.js.map
index 8f7b7b37200e76..f8922244179359 100644
--- a/packages/expo-router/build/native-tabs/types.js.map
+++ b/packages/expo-router/build/native-tabs/types.js.map
@@ -1 +1 @@
-{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/native-tabs/types.ts"],"names":[],"mappings":";;;AAsGa,QAAA,sBAAsB,GAAG;IACpC,MAAM;IACN,eAAe;IACf,YAAY;IACZ,OAAO;IACP,MAAM;IACN,SAAS;IACT,WAAW;IACX,yBAAyB;IACzB,oBAAoB;IACpB,gBAAgB;IAChB,qBAAqB;IACrB,sBAAsB;IACtB,8BAA8B;IAC9B,yBAAyB;IACzB,qBAAqB;IACrB,0BAA0B;IAC1B,2BAA2B;IAC3B,6BAA6B;IAC7B,wBAAwB;IACxB,oBAAoB;IACpB,yBAAyB;IACzB,0BAA0B;CAClB,CAAC;AA2KE,QAAA,6CAA6C,GAAG;IAC3D,MAAM;IACN,UAAU;IACV,SAAS;IACT,WAAW;CACH,CAAC;AAUE,QAAA,oCAAoC,GAAG;IAClD,WAAW;IACX,OAAO;IACP,cAAc;IACd,YAAY;CACJ,CAAC;AA8FX,MAAM,4BAA4B,GAAG;IACnC,WAAW;IACX,UAAU;IACV,WAAW;IACX,WAAW;IACX,UAAU;IACV,SAAS;IACT,MAAM;IACN,YAAY;IACZ,YAAY;IACZ,SAAS;IACT,QAAQ;IACR,UAAU;CACF,CAAC","sourcesContent":["import type { DefaultRouterOptions } from '@react-navigation/native';\nimport type { PropsWithChildren } from 'react';\nimport type {\n ColorValue,\n ImageSourcePropType,\n StyleProp,\n TextStyle,\n ViewStyle,\n} from 'react-native';\nimport type { BottomTabsScreenProps } from 'react-native-screens';\nimport type { SFSymbol } from 'sf-symbols-typescript';\n\nexport type NativeScreenProps = Partial>;\n\nexport interface NativeTabOptions extends DefaultRouterOptions {\n icon?: SymbolOrImageSource;\n selectedIcon?: SymbolOrImageSource;\n title?: string;\n badgeValue?: string;\n selectedLabelStyle?: NativeTabsLabelStyle;\n labelStyle?: NativeTabsLabelStyle;\n role?: NativeTabsTabBarItemRole;\n selectedIconColor?: ColorValue;\n selectedBadgeBackgroundColor?: ColorValue;\n badgeBackgroundColor?: ColorValue;\n badgeTextColor?: ColorValue;\n backgroundColor?: ColorValue;\n blurEffect?: NativeTabsBlurEffect;\n shadowColor?: ColorValue;\n iconColor?: ColorValue;\n disableTransparentOnScrollEdge?: boolean;\n titlePositionAdjustment?: {\n horizontal?: number;\n vertical?: number;\n };\n selectedTitlePositionAdjustment?: {\n horizontal?: number;\n vertical?: number;\n };\n indicatorColor?: ColorValue;\n hidden?: boolean;\n specialEffects?: BottomTabsScreenProps['specialEffects'];\n nativeProps?: NativeScreenProps;\n disableAutomaticContentInsets?: boolean;\n contentStyle?: Pick<\n ViewStyle,\n | 'backgroundColor'\n | 'experimental_backgroundImage'\n | 'padding'\n | 'paddingTop'\n | 'paddingBottom'\n | 'paddingLeft'\n | 'paddingRight'\n | 'paddingBlock'\n | 'paddingBlockEnd'\n | 'paddingBlockStart'\n | 'paddingInline'\n | 'paddingInlineEnd'\n | 'paddingInlineStart'\n | 'paddingEnd'\n | 'paddingHorizontal'\n | 'paddingVertical'\n | 'paddingStart'\n | 'alignContent'\n | 'alignItems'\n | 'justifyContent'\n | 'flexDirection'\n | 'gap'\n >;\n}\n\nexport type SymbolOrImageSource =\n | {\n /**\n * The name of the SF Symbol to use as an icon.\n * @platform iOS\n */\n sf?: SFSymbol;\n /**\n * The name of the drawable resource to use as an icon.\n * @platform android\n */\n drawable?: string;\n }\n | {\n /**\n * The image source to use as an icon.\n */\n src?: ImageSourcePropType | Promise;\n /**\n * Controls how the icon is rendered on iOS.\n * @platform ios\n * @default 'template'\n */\n renderingMode?: 'template' | 'original';\n };\n\nexport type NativeTabsLabelStyle = Pick<\n TextStyle,\n 'fontFamily' | 'fontSize' | 'fontStyle' | 'fontWeight' | 'color'\n>;\n\nexport const SUPPORTED_BLUR_EFFECTS = [\n 'none',\n 'systemDefault',\n 'extraLight',\n 'light',\n 'dark',\n 'regular',\n 'prominent',\n 'systemUltraThinMaterial',\n 'systemThinMaterial',\n 'systemMaterial',\n 'systemThickMaterial',\n 'systemChromeMaterial',\n 'systemUltraThinMaterialLight',\n 'systemThinMaterialLight',\n 'systemMaterialLight',\n 'systemThickMaterialLight',\n 'systemChromeMaterialLight',\n 'systemUltraThinMaterialDark',\n 'systemThinMaterialDark',\n 'systemMaterialDark',\n 'systemThickMaterialDark',\n 'systemChromeMaterialDark',\n] as const;\n\n/**\n * @see [Apple documentation](https://developer.apple.com/documentation/uikit/uiblureffect/style)\n */\nexport type NativeTabsBlurEffect = (typeof SUPPORTED_BLUR_EFFECTS)[number];\n\nexport interface NativeTabsProps extends PropsWithChildren {\n // #region common props\n /**\n * The style of the every tab label in the tab bar.\n */\n labelStyle?:\n | StyleProp\n | {\n default?: StyleProp;\n selected?: StyleProp;\n };\n /**\n * The color of every tab icon in the tab bar.\n */\n iconColor?: ColorValue | { default?: ColorValue; selected?: ColorValue };\n /**\n * The tint color of the tab icon.\n *\n * Can be overridden by icon color and label color for each tab individually.\n */\n tintColor?: ColorValue;\n /**\n * The background color of the tab bar.\n */\n backgroundColor?: ColorValue;\n /**\n * The background color of every badge in the tab bar.\n */\n badgeBackgroundColor?: ColorValue;\n /**\n * When set to `true`, hides the tab bar.\n *\n * @default false\n */\n hidden?: boolean;\n // #endregion common props\n // #region iOS props\n /**\n * Specifies the minimize behavior for the tab bar.\n *\n * Available starting from iOS 26.\n *\n * The following values are currently supported:\n *\n * - `automatic` - resolves to the system default minimize behavior\n * - `never` - the tab bar does not minimize\n * - `onScrollDown` - the tab bar minimizes when scrolling down and\n * expands when scrolling back up\n * - `onScrollUp` - the tab bar minimizes when scrolling up and expands\n * when scrolling back down\n *\n * @see The supported values correspond to the official [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbarcontroller/minimizebehavior).\n *\n * @default automatic\n *\n * @platform iOS 26+\n */\n minimizeBehavior?: NativeTabsTabBarMinimizeBehavior;\n /**\n * The blur effect applied to the tab bar.\n *\n * @platform iOS\n */\n blurEffect?: NativeTabsBlurEffect;\n /**\n * The color of the shadow.\n *\n * @see [Apple documentation](https://developer.apple.com/documentation/uikit/uibarappearance/shadowcolor)\n *\n * @platform iOS\n */\n shadowColor?: ColorValue;\n /**\n * @see [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbaritem/titlepositionadjustment)\n *\n * @platform iOS\n */\n titlePositionAdjustment?: {\n horizontal?: number;\n vertical?: number;\n };\n /**\n * When set to `true`, the tab bar will not become transparent when scrolled to the edge.\n *\n * @platform iOS\n */\n disableTransparentOnScrollEdge?: boolean;\n /**\n * When set to `true`, enables the sidebarAdaptable tab bar style on iPadOS and macOS. This prop has no effect on iPhone.\n *\n * @platform iOS 18+\n */\n sidebarAdaptable?: boolean;\n // #endregion iOS props\n // #region android props\n /**\n * Disables the active indicator for the tab bar.\n *\n * @platform android\n */\n disableIndicator?: boolean;\n /**\n * The behavior when navigating back with the back button.\n *\n * @platform android\n */\n backBehavior?: 'none' | 'initialRoute' | 'history';\n /**\n * The visibility mode of the tab item label.\n *\n * @see [Material Components documentation](https://github.com/material-components/material-components-android/blob/master/docs/components/BottomNavigation.md#making-navigation-bar-accessible)\n *\n * @platform android\n */\n labelVisibilityMode?: NativeTabsTabBarItemLabelVisibilityMode;\n /**\n * The color of the ripple effect when the tab is pressed.\n *\n * @platform android\n */\n rippleColor?: ColorValue;\n /**\n * The color of the tab indicator.\n *\n * @platform android\n * @platform web\n */\n indicatorColor?: ColorValue;\n /**\n * The color of the badge text.\n *\n * @platform android\n * @platform web\n */\n badgeTextColor?: ColorValue;\n // #endregion android props\n}\n\nexport interface InternalNativeTabsProps extends NativeTabsProps {\n nonTriggerChildren?: React.ReactNode;\n}\nexport interface NativeTabsViewProps\n extends Omit<\n InternalNativeTabsProps,\n | 'labelStyle'\n | 'iconColor'\n | 'backgroundColor'\n | 'badgeBackgroundColor'\n | 'blurEffect'\n | 'indicatorColor'\n | 'badgeTextColor'\n > {\n focusedIndex: number;\n tabs: NativeTabsViewTabItem[];\n onTabChange: (tabKey: string) => void;\n}\n\nexport interface NativeTabsViewTabItem {\n options: NativeTabOptions;\n routeKey: string;\n name: string;\n contentRenderer: () => React.ReactNode;\n}\n\nexport const SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES = [\n 'auto',\n 'selected',\n 'labeled',\n 'unlabeled',\n] as const;\n\n/**\n * @see [Material Components documentation](https://github.com/material-components/material-components-android/blob/master/docs/components/BottomNavigation.md#making-navigation-bar-accessible)\n *\n * @platform android\n */\nexport type NativeTabsTabBarItemLabelVisibilityMode =\n (typeof SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES)[number];\n\nexport const SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS = [\n 'automatic',\n 'never',\n 'onScrollDown',\n 'onScrollUp',\n] as const;\n\n/**\n * @see [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbarcontroller/minimizebehavior)\n *\n * @platform iOS 26\n */\nexport type NativeTabsTabBarMinimizeBehavior =\n (typeof SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS)[number];\n\nexport interface NativeTabTriggerProps {\n /**\n * The name of the route.\n *\n * This is required when used inside a Layout component.\n *\n * When used in a route it has no effect.\n */\n name?: string;\n /**\n * If true, the tab will be hidden from the tab bar.\n *\n * > **Note**: Marking a tab as `hidden` means it cannot be navigated to in any way.\n */\n hidden?: boolean;\n /**\n * Props passed to the underlying native tab screen implementation.\n * Use this to configure props not directly exposed by Expo Router, but available in `react-native-screens`.\n *\n * > **Note**: This will override any other props set by Expo Router and may lead to unexpected behavior.\n *\n * > **Note**: This is an unstable API and may change or be removed in minor versions.\n *\n * @platform android\n * @platform iOS\n */\n unstable_nativeProps?: NativeScreenProps;\n /**\n * If true, the tab will not pop stack to the root when selected again.\n *\n * @default false\n * @platform iOS\n */\n disablePopToTop?: boolean;\n /**\n * If true, the tab will not scroll to the top when selected again.\n * @default false\n *\n * @platform iOS\n */\n disableScrollToTop?: boolean;\n /**\n * The children of the trigger.\n *\n * Use `Icon`, `Label`, and `Badge` components to customize the tab.\n */\n children?: React.ReactNode;\n /**\n * System-provided tab bar item with predefined icon and title\n *\n * Uses Apple's built-in tab bar items (e.g., bookmarks, contacts, downloads) with\n * standard iOS styling and localized titles. Custom `icon` or `selectedIcon`\n * properties will override the system icon, but the system-defined title cannot\n * be customized.\n *\n * @see The supported values correspond to the official [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbaritem/systemitem).\n * @platform ios\n */\n role?: NativeTabsTabBarItemRole;\n /**\n * The default behavior differs between iOS and Android.\n *\n * On **Android**, the content of a native tabs screen is automatically wrapped in a `SafeAreaView`,\n * and the **bottom** inset is applied. Other insets must be handled manually.\n *\n * On **iOS**, the first scroll view nested inside a native tabs screen has\n * [automatic content inset adjustment](https://reactnative.dev/docs/scrollview#contentinsetadjustmentbehavior-ios) enabled\n *\n * When this property is set to `true`, automatic content inset adjustment is disabled for the screen\n * and must be managed manually. You can use `SafeAreaView` from `react-native-screens/experimental`\n * to handle safe area insets.\n *\n * @platform android\n * @platform ios\n */\n disableAutomaticContentInsets?: boolean;\n /**\n * The style applied to the content of the tab\n *\n * Note: Only certain style properties are supported.\n */\n contentStyle?: NativeTabOptions['contentStyle'];\n}\n\nconst SUPPORTED_TAB_BAR_ITEM_ROLES = [\n 'bookmarks',\n 'contacts',\n 'downloads',\n 'favorites',\n 'featured',\n 'history',\n 'more',\n 'mostRecent',\n 'mostViewed',\n 'recents',\n 'search',\n 'topRated',\n] as const;\n\nexport type NativeTabsTabBarItemRole = (typeof SUPPORTED_TAB_BAR_ITEM_ROLES)[number];\n"]}
\ No newline at end of file
+{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/native-tabs/types.ts"],"names":[],"mappings":";;;AAsGa,QAAA,sBAAsB,GAAG;IACpC,MAAM;IACN,eAAe;IACf,YAAY;IACZ,OAAO;IACP,MAAM;IACN,SAAS;IACT,WAAW;IACX,yBAAyB;IACzB,oBAAoB;IACpB,gBAAgB;IAChB,qBAAqB;IACrB,sBAAsB;IACtB,8BAA8B;IAC9B,yBAAyB;IACzB,qBAAqB;IACrB,0BAA0B;IAC1B,2BAA2B;IAC3B,6BAA6B;IAC7B,wBAAwB;IACxB,oBAAoB;IACpB,yBAAyB;IACzB,0BAA0B;CAClB,CAAC;AA2KE,QAAA,6CAA6C,GAAG;IAC3D,MAAM;IACN,UAAU;IACV,SAAS;IACT,WAAW;CACH,CAAC;AAUE,QAAA,oCAAoC,GAAG;IAClD,WAAW;IACX,OAAO;IACP,cAAc;IACd,YAAY;CACJ,CAAC;AA8FX,MAAM,4BAA4B,GAAG;IACnC,WAAW;IACX,UAAU;IACV,WAAW;IACX,WAAW;IACX,UAAU;IACV,SAAS;IACT,MAAM;IACN,YAAY;IACZ,YAAY;IACZ,SAAS;IACT,QAAQ;IACR,UAAU;CACF,CAAC","sourcesContent":["import type { DefaultRouterOptions } from '@react-navigation/native';\nimport type { PropsWithChildren } from 'react';\nimport type {\n ColorValue,\n ImageSourcePropType,\n StyleProp,\n TextStyle,\n ViewStyle,\n} from 'react-native';\nimport type { TabsScreenProps } from 'react-native-screens';\nimport type { SFSymbol } from 'sf-symbols-typescript';\n\nexport type NativeScreenProps = Partial>;\n\nexport interface NativeTabOptions extends DefaultRouterOptions {\n icon?: SymbolOrImageSource;\n selectedIcon?: SymbolOrImageSource;\n title?: string;\n badgeValue?: string;\n selectedLabelStyle?: NativeTabsLabelStyle;\n labelStyle?: NativeTabsLabelStyle;\n role?: NativeTabsTabBarItemRole;\n selectedIconColor?: ColorValue;\n selectedBadgeBackgroundColor?: ColorValue;\n badgeBackgroundColor?: ColorValue;\n badgeTextColor?: ColorValue;\n backgroundColor?: ColorValue;\n blurEffect?: NativeTabsBlurEffect;\n shadowColor?: ColorValue;\n iconColor?: ColorValue;\n disableTransparentOnScrollEdge?: boolean;\n titlePositionAdjustment?: {\n horizontal?: number;\n vertical?: number;\n };\n selectedTitlePositionAdjustment?: {\n horizontal?: number;\n vertical?: number;\n };\n indicatorColor?: ColorValue;\n hidden?: boolean;\n specialEffects?: TabsScreenProps['specialEffects'];\n nativeProps?: NativeScreenProps;\n disableAutomaticContentInsets?: boolean;\n contentStyle?: Pick<\n ViewStyle,\n | 'backgroundColor'\n | 'experimental_backgroundImage'\n | 'padding'\n | 'paddingTop'\n | 'paddingBottom'\n | 'paddingLeft'\n | 'paddingRight'\n | 'paddingBlock'\n | 'paddingBlockEnd'\n | 'paddingBlockStart'\n | 'paddingInline'\n | 'paddingInlineEnd'\n | 'paddingInlineStart'\n | 'paddingEnd'\n | 'paddingHorizontal'\n | 'paddingVertical'\n | 'paddingStart'\n | 'alignContent'\n | 'alignItems'\n | 'justifyContent'\n | 'flexDirection'\n | 'gap'\n >;\n}\n\nexport type SymbolOrImageSource =\n | {\n /**\n * The name of the SF Symbol to use as an icon.\n * @platform iOS\n */\n sf?: SFSymbol;\n /**\n * The name of the drawable resource to use as an icon.\n * @platform android\n */\n drawable?: string;\n }\n | {\n /**\n * The image source to use as an icon.\n */\n src?: ImageSourcePropType | Promise;\n /**\n * Controls how the icon is rendered on iOS.\n * @platform ios\n * @default 'template'\n */\n renderingMode?: 'template' | 'original';\n };\n\nexport type NativeTabsLabelStyle = Pick<\n TextStyle,\n 'fontFamily' | 'fontSize' | 'fontStyle' | 'fontWeight' | 'color'\n>;\n\nexport const SUPPORTED_BLUR_EFFECTS = [\n 'none',\n 'systemDefault',\n 'extraLight',\n 'light',\n 'dark',\n 'regular',\n 'prominent',\n 'systemUltraThinMaterial',\n 'systemThinMaterial',\n 'systemMaterial',\n 'systemThickMaterial',\n 'systemChromeMaterial',\n 'systemUltraThinMaterialLight',\n 'systemThinMaterialLight',\n 'systemMaterialLight',\n 'systemThickMaterialLight',\n 'systemChromeMaterialLight',\n 'systemUltraThinMaterialDark',\n 'systemThinMaterialDark',\n 'systemMaterialDark',\n 'systemThickMaterialDark',\n 'systemChromeMaterialDark',\n] as const;\n\n/**\n * @see [Apple documentation](https://developer.apple.com/documentation/uikit/uiblureffect/style)\n */\nexport type NativeTabsBlurEffect = (typeof SUPPORTED_BLUR_EFFECTS)[number];\n\nexport interface NativeTabsProps extends PropsWithChildren {\n // #region common props\n /**\n * The style of the every tab label in the tab bar.\n */\n labelStyle?:\n | StyleProp\n | {\n default?: StyleProp;\n selected?: StyleProp;\n };\n /**\n * The color of every tab icon in the tab bar.\n */\n iconColor?: ColorValue | { default?: ColorValue; selected?: ColorValue };\n /**\n * The tint color of the tab icon.\n *\n * Can be overridden by icon color and label color for each tab individually.\n */\n tintColor?: ColorValue;\n /**\n * The background color of the tab bar.\n */\n backgroundColor?: ColorValue;\n /**\n * The background color of every badge in the tab bar.\n */\n badgeBackgroundColor?: ColorValue;\n /**\n * When set to `true`, hides the tab bar.\n *\n * @default false\n */\n hidden?: boolean;\n // #endregion common props\n // #region iOS props\n /**\n * Specifies the minimize behavior for the tab bar.\n *\n * Available starting from iOS 26.\n *\n * The following values are currently supported:\n *\n * - `automatic` - resolves to the system default minimize behavior\n * - `never` - the tab bar does not minimize\n * - `onScrollDown` - the tab bar minimizes when scrolling down and\n * expands when scrolling back up\n * - `onScrollUp` - the tab bar minimizes when scrolling up and expands\n * when scrolling back down\n *\n * @see The supported values correspond to the official [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbarcontroller/minimizebehavior).\n *\n * @default automatic\n *\n * @platform iOS 26+\n */\n minimizeBehavior?: NativeTabsTabBarMinimizeBehavior;\n /**\n * The blur effect applied to the tab bar.\n *\n * @platform iOS\n */\n blurEffect?: NativeTabsBlurEffect;\n /**\n * The color of the shadow.\n *\n * @see [Apple documentation](https://developer.apple.com/documentation/uikit/uibarappearance/shadowcolor)\n *\n * @platform iOS\n */\n shadowColor?: ColorValue;\n /**\n * @see [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbaritem/titlepositionadjustment)\n *\n * @platform iOS\n */\n titlePositionAdjustment?: {\n horizontal?: number;\n vertical?: number;\n };\n /**\n * When set to `true`, the tab bar will not become transparent when scrolled to the edge.\n *\n * @platform iOS\n */\n disableTransparentOnScrollEdge?: boolean;\n /**\n * When set to `true`, enables the sidebarAdaptable tab bar style on iPadOS and macOS. This prop has no effect on iPhone.\n *\n * @platform iOS 18+\n */\n sidebarAdaptable?: boolean;\n // #endregion iOS props\n // #region android props\n /**\n * Disables the active indicator for the tab bar.\n *\n * @platform android\n */\n disableIndicator?: boolean;\n /**\n * The behavior when navigating back with the back button.\n *\n * @platform android\n */\n backBehavior?: 'none' | 'initialRoute' | 'history';\n /**\n * The visibility mode of the tab item label.\n *\n * @see [Material Components documentation](https://github.com/material-components/material-components-android/blob/master/docs/components/BottomNavigation.md#making-navigation-bar-accessible)\n *\n * @platform android\n */\n labelVisibilityMode?: NativeTabsTabBarItemLabelVisibilityMode;\n /**\n * The color of the ripple effect when the tab is pressed.\n *\n * @platform android\n */\n rippleColor?: ColorValue;\n /**\n * The color of the tab indicator.\n *\n * @platform android\n * @platform web\n */\n indicatorColor?: ColorValue;\n /**\n * The color of the badge text.\n *\n * @platform android\n * @platform web\n */\n badgeTextColor?: ColorValue;\n // #endregion android props\n}\n\nexport interface InternalNativeTabsProps extends NativeTabsProps {\n nonTriggerChildren?: React.ReactNode;\n}\nexport interface NativeTabsViewProps\n extends Omit<\n InternalNativeTabsProps,\n | 'labelStyle'\n | 'iconColor'\n | 'backgroundColor'\n | 'badgeBackgroundColor'\n | 'blurEffect'\n | 'indicatorColor'\n | 'badgeTextColor'\n > {\n focusedIndex: number;\n tabs: NativeTabsViewTabItem[];\n onTabChange: (tabKey: string) => void;\n}\n\nexport interface NativeTabsViewTabItem {\n options: NativeTabOptions;\n routeKey: string;\n name: string;\n contentRenderer: () => React.ReactNode;\n}\n\nexport const SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES = [\n 'auto',\n 'selected',\n 'labeled',\n 'unlabeled',\n] as const;\n\n/**\n * @see [Material Components documentation](https://github.com/material-components/material-components-android/blob/master/docs/components/BottomNavigation.md#making-navigation-bar-accessible)\n *\n * @platform android\n */\nexport type NativeTabsTabBarItemLabelVisibilityMode =\n (typeof SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES)[number];\n\nexport const SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS = [\n 'automatic',\n 'never',\n 'onScrollDown',\n 'onScrollUp',\n] as const;\n\n/**\n * @see [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbarcontroller/minimizebehavior)\n *\n * @platform iOS 26\n */\nexport type NativeTabsTabBarMinimizeBehavior =\n (typeof SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS)[number];\n\nexport interface NativeTabTriggerProps {\n /**\n * The name of the route.\n *\n * This is required when used inside a Layout component.\n *\n * When used in a route it has no effect.\n */\n name?: string;\n /**\n * If true, the tab will be hidden from the tab bar.\n *\n * > **Note**: Marking a tab as `hidden` means it cannot be navigated to in any way.\n */\n hidden?: boolean;\n /**\n * Props passed to the underlying native tab screen implementation.\n * Use this to configure props not directly exposed by Expo Router, but available in `react-native-screens`.\n *\n * > **Note**: This will override any other props set by Expo Router and may lead to unexpected behavior.\n *\n * > **Note**: This is an unstable API and may change or be removed in minor versions.\n *\n * @platform android\n * @platform iOS\n */\n unstable_nativeProps?: NativeScreenProps;\n /**\n * If true, the tab will not pop stack to the root when selected again.\n *\n * @default false\n * @platform iOS\n */\n disablePopToTop?: boolean;\n /**\n * If true, the tab will not scroll to the top when selected again.\n * @default false\n *\n * @platform iOS\n */\n disableScrollToTop?: boolean;\n /**\n * The children of the trigger.\n *\n * Use `Icon`, `Label`, and `Badge` components to customize the tab.\n */\n children?: React.ReactNode;\n /**\n * System-provided tab bar item with predefined icon and title\n *\n * Uses Apple's built-in tab bar items (e.g., bookmarks, contacts, downloads) with\n * standard iOS styling and localized titles. Custom `icon` or `selectedIcon`\n * properties will override the system icon, but the system-defined title cannot\n * be customized.\n *\n * @see The supported values correspond to the official [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbaritem/systemitem).\n * @platform ios\n */\n role?: NativeTabsTabBarItemRole;\n /**\n * The default behavior differs between iOS and Android.\n *\n * On **Android**, the content of a native tabs screen is automatically wrapped in a `SafeAreaView`,\n * and the **bottom** inset is applied. Other insets must be handled manually.\n *\n * On **iOS**, the first scroll view nested inside a native tabs screen has\n * [automatic content inset adjustment](https://reactnative.dev/docs/scrollview#contentinsetadjustmentbehavior-ios) enabled\n *\n * When this property is set to `true`, automatic content inset adjustment is disabled for the screen\n * and must be managed manually. You can use `SafeAreaView` from `react-native-screens/experimental`\n * to handle safe area insets.\n *\n * @platform android\n * @platform ios\n */\n disableAutomaticContentInsets?: boolean;\n /**\n * The style applied to the content of the tab\n *\n * Note: Only certain style properties are supported.\n */\n contentStyle?: NativeTabOptions['contentStyle'];\n}\n\nconst SUPPORTED_TAB_BAR_ITEM_ROLES = [\n 'bookmarks',\n 'contacts',\n 'downloads',\n 'favorites',\n 'featured',\n 'history',\n 'more',\n 'mostRecent',\n 'mostViewed',\n 'recents',\n 'search',\n 'topRated',\n] as const;\n\nexport type NativeTabsTabBarItemRole = (typeof SUPPORTED_TAB_BAR_ITEM_ROLES)[number];\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/utils/bottomAccessory.d.ts b/packages/expo-router/build/native-tabs/utils/bottomAccessory.d.ts
index d7586c71b42f4b..e36b6fcb6031dd 100644
--- a/packages/expo-router/build/native-tabs/utils/bottomAccessory.d.ts
+++ b/packages/expo-router/build/native-tabs/utils/bottomAccessory.d.ts
@@ -1,9 +1,9 @@
import { type ReactElement } from 'react';
-import type { BottomAccessoryFn } from 'react-native-screens';
+import type { TabAccessoryComponentFactory } from 'react-native-screens';
import type { NativeTabsBottomAccessoryProps } from '../common/elements';
/**
* Converts `` component into a function,
* which can be used by `react-native-screens` to render the accessory.
*/
-export declare function useBottomAccessoryFunctionFromBottomAccessories(bottomAccessory: ReactElement> | undefined): BottomAccessoryFn | undefined;
+export declare function useBottomAccessoryFunctionFromBottomAccessories(bottomAccessory: ReactElement> | undefined): TabAccessoryComponentFactory | undefined;
//# sourceMappingURL=bottomAccessory.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/utils/bottomAccessory.d.ts.map b/packages/expo-router/build/native-tabs/utils/bottomAccessory.d.ts.map
index 6e3bbb0dd93639..7eb8641080a66b 100644
--- a/packages/expo-router/build/native-tabs/utils/bottomAccessory.d.ts.map
+++ b/packages/expo-router/build/native-tabs/utils/bottomAccessory.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"bottomAccessory.d.ts","sourceRoot":"","sources":["../../../src/native-tabs/utils/bottomAccessory.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAGzE;;;GAGG;AACH,wBAAgB,+CAA+C,CAC7D,eAAe,EACX,YAAY,CAAC,8BAA8B,EAAE,MAAM,GAAG,KAAK,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,GACvF,SAAS,GACZ,iBAAiB,GAAG,SAAS,CAY/B"}
\ No newline at end of file
+{"version":3,"file":"bottomAccessory.d.ts","sourceRoot":"","sources":["../../../src/native-tabs/utils/bottomAccessory.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAEzE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAGzE;;;GAGG;AACH,wBAAgB,+CAA+C,CAC7D,eAAe,EACX,YAAY,CAAC,8BAA8B,EAAE,MAAM,GAAG,KAAK,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,GACvF,SAAS,GACZ,4BAA4B,GAAG,SAAS,CAY1C"}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/utils/bottomAccessory.js.map b/packages/expo-router/build/native-tabs/utils/bottomAccessory.js.map
index 37ddf4b4a705b0..1d21d1f42fc140 100644
--- a/packages/expo-router/build/native-tabs/utils/bottomAccessory.js.map
+++ b/packages/expo-router/build/native-tabs/utils/bottomAccessory.js.map
@@ -1 +1 @@
-{"version":3,"file":"bottomAccessory.js","sourceRoot":"","sources":["../../../src/native-tabs/utils/bottomAccessory.tsx"],"names":[],"mappings":";;AAUA,0GAgBC;AA1BD,iCAAmD;AAInD,oCAA2D;AAE3D;;;GAGG;AACH,SAAgB,+CAA+C,CAC7D,eAEa;IAEb,OAAO,IAAA,eAAO,EACZ,GAAG,EAAE,CACH,eAAe;QACb,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CACf,CAAC,uCAA+B,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAClD;cAAA,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,CACjC;YAAA,EAAE,uCAA+B,CAAC,CACnC;QACH,CAAC,CAAC,SAAS,EACf,CAAC,eAAe,CAAC,CAClB,CAAC;AACJ,CAAC","sourcesContent":["import { useMemo, type ReactElement } from 'react';\nimport type { BottomAccessoryFn } from 'react-native-screens';\n\nimport type { NativeTabsBottomAccessoryProps } from '../common/elements';\nimport { BottomAccessoryPlacementContext } from '../hooks';\n\n/**\n * Converts `` component into a function,\n * which can be used by `react-native-screens` to render the accessory.\n */\nexport function useBottomAccessoryFunctionFromBottomAccessories(\n bottomAccessory:\n | ReactElement>\n | undefined\n): BottomAccessoryFn | undefined {\n return useMemo(\n () =>\n bottomAccessory\n ? (environment) => (\n \n {bottomAccessory.props.children}\n \n )\n : undefined,\n [bottomAccessory]\n );\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"bottomAccessory.js","sourceRoot":"","sources":["../../../src/native-tabs/utils/bottomAccessory.tsx"],"names":[],"mappings":";;AAUA,0GAgBC;AA1BD,iCAAmD;AAInD,oCAA2D;AAE3D;;;GAGG;AACH,SAAgB,+CAA+C,CAC7D,eAEa;IAEb,OAAO,IAAA,eAAO,EACZ,GAAG,EAAE,CACH,eAAe;QACb,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CACf,CAAC,uCAA+B,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAClD;cAAA,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,CACjC;YAAA,EAAE,uCAA+B,CAAC,CACnC;QACH,CAAC,CAAC,SAAS,EACf,CAAC,eAAe,CAAC,CAClB,CAAC;AACJ,CAAC","sourcesContent":["import { useMemo, type ReactElement } from 'react';\nimport type { TabAccessoryComponentFactory } from 'react-native-screens';\n\nimport type { NativeTabsBottomAccessoryProps } from '../common/elements';\nimport { BottomAccessoryPlacementContext } from '../hooks';\n\n/**\n * Converts `` component into a function,\n * which can be used by `react-native-screens` to render the accessory.\n */\nexport function useBottomAccessoryFunctionFromBottomAccessories(\n bottomAccessory:\n | ReactElement>\n | undefined\n): TabAccessoryComponentFactory | undefined {\n return useMemo(\n () =>\n bottomAccessory\n ? (environment) => (\n \n {bottomAccessory.props.children}\n \n )\n : undefined,\n [bottomAccessory]\n );\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/utils/icon.d.ts b/packages/expo-router/build/native-tabs/utils/icon.d.ts
index 6348ff8ee88946..ed5a793d42cf06 100644
--- a/packages/expo-router/build/native-tabs/utils/icon.d.ts
+++ b/packages/expo-router/build/native-tabs/utils/icon.d.ts
@@ -1,5 +1,5 @@
import type { ColorValue, ImageSourcePropType } from 'react-native';
-import type { BottomTabsScreenProps, PlatformIconAndroid, PlatformIconIOS } from 'react-native-screens';
+import type { TabsScreenProps, PlatformIconAndroid, PlatformIconIOS } from 'react-native-screens';
import type { SFSymbol } from 'sf-symbols-typescript';
import type { NativeTabOptions, NativeTabsProps } from '../types';
export declare function convertIconColorPropToObject(iconColor: NativeTabsProps['iconColor']): {
@@ -20,7 +20,7 @@ export declare function useAwaitedScreensIcon(icon: NativeTabOptions['icon']): {
src?: ImageSourcePropType;
renderingMode?: "template" | "original";
} | undefined;
-export declare function convertOptionsIconToRNScreensPropsIcon(icon: AwaitedIcon | undefined): BottomTabsScreenProps['icon'];
+export declare function convertOptionsIconToRNScreensPropsIcon(icon: AwaitedIcon | undefined): TabsScreenProps['icon'];
export declare function convertOptionsIconToIOSPropsIcon(icon: AwaitedIcon | undefined): PlatformIconIOS | undefined;
export declare function convertOptionsIconToAndroidPropsIcon(icon: AwaitedIcon): PlatformIconAndroid | undefined;
export declare function convertComponentSrcToImageSource(src: React.ReactElement): {
diff --git a/packages/expo-router/build/native-tabs/utils/icon.d.ts.map b/packages/expo-router/build/native-tabs/utils/icon.d.ts.map
index 4fbe4e265f915f..90cee578313b78 100644
--- a/packages/expo-router/build/native-tabs/utils/icon.d.ts.map
+++ b/packages/expo-router/build/native-tabs/utils/icon.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../../src/native-tabs/utils/icon.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAElE,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,eAAe,CAAC,WAAW,CAAC,GAAG;IACrF,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB,CAUA;AAED,KAAK,WAAW,GACZ;IACE,EAAE,CAAC,EAAE,QAAQ,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACD;IACE,GAAG,CAAC,EAAE,mBAAmB,CAAC;IAC1B,aAAa,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;CACzC,CAAC;AAEN,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC;;;;UAJxD,mBAAmB;oBACT,UAAU,GAAG,UAAU;cAyB5C;AAMD,wBAAgB,sCAAsC,CACpD,IAAI,EAAE,WAAW,GAAG,SAAS,GAC5B,qBAAqB,CAAC,MAAM,CAAC,CAQ/B;AAED,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,WAAW,GAAG,SAAS,GAC5B,eAAe,GAAG,SAAS,CAc7B;AAED,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,WAAW,GAChB,mBAAmB,GAAG,SAAS,CAWjC;AAED,wBAAgB,gCAAgC,CAAC,GAAG,EAAE,KAAK,CAAC,YAAY;;cAUvE"}
\ No newline at end of file
+{"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../../src/native-tabs/utils/icon.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAElE,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,eAAe,CAAC,WAAW,CAAC,GAAG;IACrF,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB,CAUA;AAED,KAAK,WAAW,GACZ;IACE,EAAE,CAAC,EAAE,QAAQ,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACD;IACE,GAAG,CAAC,EAAE,mBAAmB,CAAC;IAC1B,aAAa,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;CACzC,CAAC;AAEN,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC;;;;UAJxD,mBAAmB;oBACT,UAAU,GAAG,UAAU;cAyB5C;AAMD,wBAAgB,sCAAsC,CACpD,IAAI,EAAE,WAAW,GAAG,SAAS,GAC5B,eAAe,CAAC,MAAM,CAAC,CAQzB;AAED,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,WAAW,GAAG,SAAS,GAC5B,eAAe,GAAG,SAAS,CAc7B;AAED,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,WAAW,GAChB,mBAAmB,GAAG,SAAS,CAWjC;AAED,wBAAgB,gCAAgC,CAAC,GAAG,EAAE,KAAK,CAAC,YAAY;;cAUvE"}
\ No newline at end of file
diff --git a/packages/expo-router/build/native-tabs/utils/icon.js.map b/packages/expo-router/build/native-tabs/utils/icon.js.map
index af3d1606a96dae..5b0aebaceb7d54 100644
--- a/packages/expo-router/build/native-tabs/utils/icon.js.map
+++ b/packages/expo-router/build/native-tabs/utils/icon.js.map
@@ -1 +1 @@
-{"version":3,"file":"icon.js","sourceRoot":"","sources":["../../../src/native-tabs/utils/icon.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport type { ColorValue, ImageSourcePropType } from 'react-native';\nimport type {\n BottomTabsScreenProps,\n PlatformIconAndroid,\n PlatformIconIOS,\n} from 'react-native-screens';\nimport type { SFSymbol } from 'sf-symbols-typescript';\n\nimport { isChildOfType } from '../../utils/children';\nimport { NativeTabsTriggerPromiseIcon, NativeTabsTriggerVectorIcon } from '../common/elements';\nimport type { NativeTabOptions, NativeTabsProps } from '../types';\n\nexport function convertIconColorPropToObject(iconColor: NativeTabsProps['iconColor']): {\n default?: ColorValue;\n selected?: ColorValue;\n} {\n if (iconColor) {\n if (typeof iconColor === 'object' && ('default' in iconColor || 'selected' in iconColor)) {\n return iconColor;\n }\n return {\n default: iconColor as ColorValue,\n };\n }\n return {};\n}\n\ntype AwaitedIcon =\n | {\n sf?: SFSymbol;\n drawable?: string;\n }\n | {\n src?: ImageSourcePropType;\n renderingMode?: 'template' | 'original';\n };\n\nexport function useAwaitedScreensIcon(icon: NativeTabOptions['icon']) {\n const src = icon && typeof icon === 'object' && 'src' in icon ? icon.src : undefined;\n const [awaitedIcon, setAwaitedIcon] = useState(undefined);\n\n useEffect(() => {\n const loadIcon = async () => {\n if (src && src instanceof Promise) {\n const awaitedSrc = await src;\n if (awaitedSrc) {\n const currentAwaitedIcon = { src: awaitedSrc };\n setAwaitedIcon(currentAwaitedIcon);\n }\n }\n };\n loadIcon();\n // Checking `src` rather then icon here, to avoid unnecessary re-renders\n // The icon object can be recreated, while src should stay the same\n // In this case as we control `VectorIcon`, it will only change if `family` or `name` props change\n // So we should be safe with promise resolving\n }, [src]);\n\n return useMemo(() => (isAwaitedIcon(icon) ? icon : awaitedIcon), [awaitedIcon, icon]);\n}\n\nfunction isAwaitedIcon(icon: NativeTabOptions['icon']): icon is AwaitedIcon {\n return !icon || !('src' in icon && icon.src instanceof Promise);\n}\n\nexport function convertOptionsIconToRNScreensPropsIcon(\n icon: AwaitedIcon | undefined\n): BottomTabsScreenProps['icon'] {\n if (!icon) {\n return undefined;\n }\n return {\n ios: convertOptionsIconToIOSPropsIcon(icon),\n android: convertOptionsIconToAndroidPropsIcon(icon),\n };\n}\n\nexport function convertOptionsIconToIOSPropsIcon(\n icon: AwaitedIcon | undefined\n): PlatformIconIOS | undefined {\n if (icon && 'sf' in icon && icon.sf) {\n return {\n type: 'sfSymbol',\n name: icon.sf,\n };\n }\n if (icon && 'src' in icon && icon.src) {\n if (icon.renderingMode === 'original') {\n return { type: 'imageSource', imageSource: icon.src };\n }\n return { type: 'templateSource', templateSource: icon.src };\n }\n return undefined;\n}\n\nexport function convertOptionsIconToAndroidPropsIcon(\n icon: AwaitedIcon\n): PlatformIconAndroid | undefined {\n if (icon && 'drawable' in icon && icon.drawable) {\n return {\n type: 'drawableResource',\n name: icon.drawable,\n };\n }\n if (icon && 'src' in icon && icon.src) {\n return { type: 'imageSource', imageSource: icon.src };\n }\n return undefined;\n}\n\nexport function convertComponentSrcToImageSource(src: React.ReactElement) {\n if (isChildOfType(src, NativeTabsTriggerVectorIcon)) {\n const props = src.props;\n return { src: props.family.getImageSource(props.name, 24, 'white') };\n } else if (isChildOfType(src, NativeTabsTriggerPromiseIcon)) {\n return { src: src.props.loader() };\n } else {\n console.warn('Only VectorIcon is supported as a React element in Icon.src');\n }\n return undefined;\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"icon.js","sourceRoot":"","sources":["../../../src/native-tabs/utils/icon.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport type { ColorValue, ImageSourcePropType } from 'react-native';\nimport type { TabsScreenProps, PlatformIconAndroid, PlatformIconIOS } from 'react-native-screens';\nimport type { SFSymbol } from 'sf-symbols-typescript';\n\nimport { isChildOfType } from '../../utils/children';\nimport { NativeTabsTriggerPromiseIcon, NativeTabsTriggerVectorIcon } from '../common/elements';\nimport type { NativeTabOptions, NativeTabsProps } from '../types';\n\nexport function convertIconColorPropToObject(iconColor: NativeTabsProps['iconColor']): {\n default?: ColorValue;\n selected?: ColorValue;\n} {\n if (iconColor) {\n if (typeof iconColor === 'object' && ('default' in iconColor || 'selected' in iconColor)) {\n return iconColor;\n }\n return {\n default: iconColor as ColorValue,\n };\n }\n return {};\n}\n\ntype AwaitedIcon =\n | {\n sf?: SFSymbol;\n drawable?: string;\n }\n | {\n src?: ImageSourcePropType;\n renderingMode?: 'template' | 'original';\n };\n\nexport function useAwaitedScreensIcon(icon: NativeTabOptions['icon']) {\n const src = icon && typeof icon === 'object' && 'src' in icon ? icon.src : undefined;\n const [awaitedIcon, setAwaitedIcon] = useState(undefined);\n\n useEffect(() => {\n const loadIcon = async () => {\n if (src && src instanceof Promise) {\n const awaitedSrc = await src;\n if (awaitedSrc) {\n const currentAwaitedIcon = { src: awaitedSrc };\n setAwaitedIcon(currentAwaitedIcon);\n }\n }\n };\n loadIcon();\n // Checking `src` rather then icon here, to avoid unnecessary re-renders\n // The icon object can be recreated, while src should stay the same\n // In this case as we control `VectorIcon`, it will only change if `family` or `name` props change\n // So we should be safe with promise resolving\n }, [src]);\n\n return useMemo(() => (isAwaitedIcon(icon) ? icon : awaitedIcon), [awaitedIcon, icon]);\n}\n\nfunction isAwaitedIcon(icon: NativeTabOptions['icon']): icon is AwaitedIcon {\n return !icon || !('src' in icon && icon.src instanceof Promise);\n}\n\nexport function convertOptionsIconToRNScreensPropsIcon(\n icon: AwaitedIcon | undefined\n): TabsScreenProps['icon'] {\n if (!icon) {\n return undefined;\n }\n return {\n ios: convertOptionsIconToIOSPropsIcon(icon),\n android: convertOptionsIconToAndroidPropsIcon(icon),\n };\n}\n\nexport function convertOptionsIconToIOSPropsIcon(\n icon: AwaitedIcon | undefined\n): PlatformIconIOS | undefined {\n if (icon && 'sf' in icon && icon.sf) {\n return {\n type: 'sfSymbol',\n name: icon.sf,\n };\n }\n if (icon && 'src' in icon && icon.src) {\n if (icon.renderingMode === 'original') {\n return { type: 'imageSource', imageSource: icon.src };\n }\n return { type: 'templateSource', templateSource: icon.src };\n }\n return undefined;\n}\n\nexport function convertOptionsIconToAndroidPropsIcon(\n icon: AwaitedIcon\n): PlatformIconAndroid | undefined {\n if (icon && 'drawable' in icon && icon.drawable) {\n return {\n type: 'drawableResource',\n name: icon.drawable,\n };\n }\n if (icon && 'src' in icon && icon.src) {\n return { type: 'imageSource', imageSource: icon.src };\n }\n return undefined;\n}\n\nexport function convertComponentSrcToImageSource(src: React.ReactElement) {\n if (isChildOfType(src, NativeTabsTriggerVectorIcon)) {\n const props = src.props;\n return { src: props.family.getImageSource(props.name, 24, 'white') };\n } else if (isChildOfType(src, NativeTabsTriggerPromiseIcon)) {\n return { src: src.props.loader() };\n } else {\n console.warn('Only VectorIcon is supported as a React element in Icon.src');\n }\n return undefined;\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/split-view/elements.js b/packages/expo-router/build/split-view/elements.js
index 934818c2a66c30..9ee823789bf3c5 100644
--- a/packages/expo-router/build/split-view/elements.js
+++ b/packages/expo-router/build/split-view/elements.js
@@ -5,14 +5,14 @@ exports.SplitViewInspector = SplitViewInspector;
const react_native_safe_area_context_1 = require("react-native-safe-area-context");
const experimental_1 = require("react-native-screens/experimental");
function SplitViewColumn(props) {
- return (
+ return ({props.children}
- );
+ );
}
/**
* @platform iOS 26+
*/
function SplitViewInspector(props) {
- return {props.children};
+ return {props.children};
}
//# sourceMappingURL=elements.js.map
\ No newline at end of file
diff --git a/packages/expo-router/build/split-view/elements.js.map b/packages/expo-router/build/split-view/elements.js.map
index ee7688327cf108..e99f7cf72dc62d 100644
--- a/packages/expo-router/build/split-view/elements.js.map
+++ b/packages/expo-router/build/split-view/elements.js.map
@@ -1 +1 @@
-{"version":3,"file":"elements.js","sourceRoot":"","sources":["../../src/split-view/elements.tsx"],"names":[],"mappings":";;AAOA,0CAMC;AAKD,gDAEC;AApBD,mFAAkE;AAClE,oEAAoE;AAMpE,SAAgB,eAAe,CAAC,KAA2B;IACzD,OAAO,CACL,CAAC,8BAAe,CAAC,MAAM,CACrB;MAAA,CAAC,iDAAgB,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,iDAAgB,CACtD;IAAA,EAAE,8BAAe,CAAC,MAAM,CAAC,CAC1B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAAC,KAA2B;IAC5D,OAAO,CAAC,8BAAe,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,8BAAe,CAAC,SAAS,CAAC,CAAC;AACjF,CAAC","sourcesContent":["import { SafeAreaProvider } from 'react-native-safe-area-context';\nimport { SplitViewScreen } from 'react-native-screens/experimental';\n\nexport interface SplitViewColumnProps {\n children?: React.ReactNode;\n}\n\nexport function SplitViewColumn(props: SplitViewColumnProps) {\n return (\n \n {props.children}\n \n );\n}\n\n/**\n * @platform iOS 26+\n */\nexport function SplitViewInspector(props: SplitViewColumnProps) {\n return {props.children};\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"elements.js","sourceRoot":"","sources":["../../src/split-view/elements.tsx"],"names":[],"mappings":";;AAOA,0CAMC;AAKD,gDAEC;AApBD,mFAAkE;AAClE,oEAA0D;AAM1D,SAAgB,eAAe,CAAC,KAA2B;IACzD,OAAO,CACL,CAAC,oBAAK,CAAC,MAAM,CACX;MAAA,CAAC,iDAAgB,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,iDAAgB,CACtD;IAAA,EAAE,oBAAK,CAAC,MAAM,CAAC,CAChB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAAC,KAA2B;IAC5D,OAAO,CAAC,oBAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,oBAAK,CAAC,SAAS,CAAC,CAAC;AAC7D,CAAC","sourcesContent":["import { SafeAreaProvider } from 'react-native-safe-area-context';\nimport { Split } from 'react-native-screens/experimental';\n\nexport interface SplitViewColumnProps {\n children?: React.ReactNode;\n}\n\nexport function SplitViewColumn(props: SplitViewColumnProps) {\n return (\n \n {props.children}\n \n );\n}\n\n/**\n * @platform iOS 26+\n */\nexport function SplitViewInspector(props: SplitViewColumnProps) {\n return {props.children};\n}\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/split-view/index.d.ts b/packages/expo-router/build/split-view/index.d.ts
index 2ed4bd70ea1ecd..906b476e28d30e 100644
--- a/packages/expo-router/build/split-view/index.d.ts
+++ b/packages/expo-router/build/split-view/index.d.ts
@@ -1,4 +1,4 @@
export { SplitView, SplitViewProps } from './split-view';
export * from './elements';
-export { type SplitViewHostProps } from 'react-native-screens/experimental';
+export { type SplitHostProps } from 'react-native-screens/experimental';
//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-router/build/split-view/index.d.ts.map b/packages/expo-router/build/split-view/index.d.ts.map
index 103499d0baa173..a280c1261a4f2e 100644
--- a/packages/expo-router/build/split-view/index.d.ts.map
+++ b/packages/expo-router/build/split-view/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/split-view/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACzD,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,mCAAmC,CAAC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/split-view/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACzD,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-router/build/split-view/index.js.map b/packages/expo-router/build/split-view/index.js.map
index 5a6066227f9734..f293e85a0346ce 100644
--- a/packages/expo-router/build/split-view/index.js.map
+++ b/packages/expo-router/build/split-view/index.js.map
@@ -1 +1 @@
-{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/split-view/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2CAAyD;AAAhD,uGAAA,SAAS,OAAA;AAClB,6CAA2B","sourcesContent":["export { SplitView, SplitViewProps } from './split-view';\nexport * from './elements';\nexport { type SplitViewHostProps } from 'react-native-screens/experimental';\n"]}
\ No newline at end of file
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/split-view/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2CAAyD;AAAhD,uGAAA,SAAS,OAAA;AAClB,6CAA2B","sourcesContent":["export { SplitView, SplitViewProps } from './split-view';\nexport * from './elements';\nexport { type SplitHostProps } from 'react-native-screens/experimental';\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/build/split-view/split-view.d.ts b/packages/expo-router/build/split-view/split-view.d.ts
index 966a88fffd2ecc..f5f1c0aa1b05c5 100644
--- a/packages/expo-router/build/split-view/split-view.d.ts
+++ b/packages/expo-router/build/split-view/split-view.d.ts
@@ -1,10 +1,10 @@
import React, { type ReactNode } from 'react';
-import { type SplitViewHostProps } from 'react-native-screens/experimental';
+import { type SplitHostProps } from 'react-native-screens/experimental';
import { SplitViewColumn, SplitViewInspector } from './elements';
/**
- * For full list of supported props, see [`SplitViewHostProps`](https://github.com/software-mansion/react-native-screens/blob/main/src/components/gamma/split-view/SplitViewHost.types.ts#L124)
+ * For full list of supported props, see [`SplitHostProps`](http://github.com/software-mansion/react-native-screens/blob/main/src/components/gamma/split/SplitHost.types.ts#L117)
*/
-export interface SplitViewProps extends Omit {
+export interface SplitViewProps extends Omit {
children?: ReactNode;
}
declare function SplitViewNavigator({ children, ...splitViewHostProps }: SplitViewProps): React.JSX.Element;
diff --git a/packages/expo-router/build/split-view/split-view.d.ts.map b/packages/expo-router/build/split-view/split-view.d.ts.map
index 39afdaffdc264d..404f6ced1e4664 100644
--- a/packages/expo-router/build/split-view/split-view.d.ts.map
+++ b/packages/expo-router/build/split-view/split-view.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"split-view.d.ts","sourceRoot":"","sources":["../../src/split-view/split-view.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClF,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMjE;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC;IAC1E,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,iBAAS,kBAAkB,CAAC,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAE,EAAE,cAAc,qBA0D9E;AAED,eAAO,MAAM,SAAS;;;CAGpB,CAAC"}
\ No newline at end of file
+{"version":3,"file":"split-view.d.ts","sourceRoot":"","sources":["../../src/split-view/split-view.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClF,OAAO,EAAS,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAE/E,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMjE;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC;IACtE,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,iBAAS,kBAAkB,CAAC,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAE,EAAE,cAAc,qBA0D9E;AAED,eAAO,MAAM,SAAS;;;CAGpB,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-router/build/split-view/split-view.js b/packages/expo-router/build/split-view/split-view.js
index 224d7c03dc2426..5edd95af9c38db 100644
--- a/packages/expo-router/build/split-view/split-view.js
+++ b/packages/expo-router/build/split-view/split-view.js
@@ -71,13 +71,13 @@ function SplitViewNavigator({ children, ...splitViewHostProps }) {
return ;
}
// The key is needed, because number of columns cannot be changed dynamically
- return (
+ return (
{columnChildren}
-
+
-
+
{inspectorChildren}
- );
+ );
}
exports.SplitView = Object.assign(SplitViewNavigator, {
Column: elements_1.SplitViewColumn,
diff --git a/packages/expo-router/build/split-view/split-view.js.map b/packages/expo-router/build/split-view/split-view.js.map
index 3733011337147f..b8bbf2d9975c46 100644
--- a/packages/expo-router/build/split-view/split-view.js.map
+++ b/packages/expo-router/build/split-view/split-view.js.map
@@ -1 +1 @@
-{"version":3,"file":"split-view.js","sourceRoot":"","sources":["../../src/split-view/split-view.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAkF;AAClF,oEAI2C;AAE3C,yCAAiE;AACjE,4EAAyE;AACzE,kDAA0C;AAE1C,MAAM,wBAAwB,GAAG,IAAA,qBAAa,EAAC,KAAK,CAAC,CAAC;AAStD,SAAS,kBAAkB,CAAC,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAkB;IAC7E,IAAI,IAAA,WAAG,EAAC,wBAAwB,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IAED,sFAAsF;IACtF,IAAI,IAAA,WAAG,EAAC,6CAAqB,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IACzF,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAClC,OAAO,CAAC,IAAI,CACV,yGAAyG,CAC1G,CAAC;QACF,OAAO,CAAC,gBAAI,CAAC,AAAD,EAAG,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,CACxB,CAAC,6CAAqB,CAAC,KAAK,CAC1B;MAAA,CAAC,gBAAI,CAAC,AAAD,EACP;IAAA,EAAE,6CAAqB,CAAC,CACzB,CAAC;IAEF,MAAM,gBAAgB,GAAG,eAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAC5C,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,sBAAc,EAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,0BAAe,CACnE,CAAC;IACF,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,MAAM,CAC/C,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,sBAAc,EAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,6BAAkB,CACtE,CAAC;IACF,MAAM,gBAAgB,GAAG,cAAc,CAAC,MAAM,CAAC;IAC/C,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAEpD,IAAI,gBAAgB,CAAC,MAAM,KAAK,cAAc,CAAC,MAAM,GAAG,iBAAiB,CAAC,MAAM,EAAE,CAAC;QACjF,OAAO,CAAC,IAAI,CACV,uGAAuG,CACxG,CAAC;IACJ,CAAC;IAED,IAAI,gBAAgB,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,gBAAgB,GAAG,kBAAkB,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;QAChF,OAAO,CAAC,gBAAI,CAAC,AAAD,EAAG,CAAC;IAClB,CAAC;IAED,6EAA6E;IAC7E,OAAO,CACL,CAAC,4BAAa,CAAC,GAAG,CAAC,CAAC,gBAAgB,GAAG,kBAAkB,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAChF;MAAA,CAAC,cAAc,CACf;MAAA,CAAC,8BAAe,CAAC,MAAM,CACrB;QAAA,CAAC,WAAW,CAAC,AAAD,EACd;MAAA,EAAE,8BAAe,CAAC,MAAM,CACxB;MAAA,CAAC,iBAAiB,CACpB;IAAA,EAAE,4BAAa,CAAC,CACjB,CAAC;AACJ,CAAC;AAEY,QAAA,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE;IACzD,MAAM,EAAE,0BAAe;IACvB,SAAS,EAAE,6BAAkB;CAC9B,CAAC,CAAC","sourcesContent":["import React, { createContext, isValidElement, use, type ReactNode } from 'react';\nimport {\n SplitViewHost,\n SplitViewScreen,\n type SplitViewHostProps,\n} from 'react-native-screens/experimental';\n\nimport { SplitViewColumn, SplitViewInspector } from './elements';\nimport { IsWithinLayoutContext } from '../layouts/IsWithinLayoutContext';\nimport { Slot } from '../views/Navigator';\n\nconst IsWithinSplitViewContext = createContext(false);\n\n/**\n * For full list of supported props, see [`SplitViewHostProps`](https://github.com/software-mansion/react-native-screens/blob/main/src/components/gamma/split-view/SplitViewHost.types.ts#L124)\n */\nexport interface SplitViewProps extends Omit {\n children?: ReactNode;\n}\n\nfunction SplitViewNavigator({ children, ...splitViewHostProps }: SplitViewProps) {\n if (use(IsWithinSplitViewContext)) {\n throw new Error('There can only be one SplitView in the navigation hierarchy.');\n }\n\n // TODO: Add better way of detecting if SplitView is rendered inside Native navigator.\n if (use(IsWithinLayoutContext)) {\n throw new Error('SplitView cannot be used inside another navigator, except for Slot.');\n }\n\n if (process.env.EXPO_OS !== 'ios') {\n console.warn(\n 'SplitView is only supported on iOS. The SplitView will behave like a Slot navigator on other platforms.'\n );\n return ;\n }\n\n const WrappedSlot = () => (\n \n \n \n );\n\n const allChildrenArray = React.Children.toArray(children);\n const columnChildren = allChildrenArray.filter(\n (child) => isValidElement(child) && child.type === SplitViewColumn\n );\n const inspectorChildren = allChildrenArray.filter(\n (child) => isValidElement(child) && child.type === SplitViewInspector\n );\n const numberOfSidebars = columnChildren.length;\n const numberOfInspectors = inspectorChildren.length;\n\n if (allChildrenArray.length !== columnChildren.length + inspectorChildren.length) {\n console.warn(\n 'Only SplitView.Column and SplitView.Inspector components are allowed as direct children of SplitView.'\n );\n }\n\n if (numberOfSidebars > 2) {\n throw new Error('There can only be two SplitView.Column in the SplitView.');\n }\n\n if (numberOfSidebars + numberOfInspectors === 0) {\n console.warn('No SplitView.Column and SplitView.Inspector found in SplitView.');\n return ;\n }\n\n // The key is needed, because number of columns cannot be changed dynamically\n return (\n \n {columnChildren}\n \n \n \n {inspectorChildren}\n \n );\n}\n\nexport const SplitView = Object.assign(SplitViewNavigator, {\n Column: SplitViewColumn,\n Inspector: SplitViewInspector,\n});\n"]}
\ No newline at end of file
+{"version":3,"file":"split-view.js","sourceRoot":"","sources":["../../src/split-view/split-view.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAkF;AAClF,oEAA+E;AAE/E,yCAAiE;AACjE,4EAAyE;AACzE,kDAA0C;AAE1C,MAAM,wBAAwB,GAAG,IAAA,qBAAa,EAAC,KAAK,CAAC,CAAC;AAStD,SAAS,kBAAkB,CAAC,EAAE,QAAQ,EAAE,GAAG,kBAAkB,EAAkB;IAC7E,IAAI,IAAA,WAAG,EAAC,wBAAwB,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IAED,sFAAsF;IACtF,IAAI,IAAA,WAAG,EAAC,6CAAqB,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IACzF,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAClC,OAAO,CAAC,IAAI,CACV,yGAAyG,CAC1G,CAAC;QACF,OAAO,CAAC,gBAAI,CAAC,AAAD,EAAG,CAAC;IAClB,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,CACxB,CAAC,6CAAqB,CAAC,KAAK,CAC1B;MAAA,CAAC,gBAAI,CAAC,AAAD,EACP;IAAA,EAAE,6CAAqB,CAAC,CACzB,CAAC;IAEF,MAAM,gBAAgB,GAAG,eAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAC5C,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,sBAAc,EAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,0BAAe,CACnE,CAAC;IACF,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,MAAM,CAC/C,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,sBAAc,EAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,6BAAkB,CACtE,CAAC;IACF,MAAM,gBAAgB,GAAG,cAAc,CAAC,MAAM,CAAC;IAC/C,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAEpD,IAAI,gBAAgB,CAAC,MAAM,KAAK,cAAc,CAAC,MAAM,GAAG,iBAAiB,CAAC,MAAM,EAAE,CAAC;QACjF,OAAO,CAAC,IAAI,CACV,uGAAuG,CACxG,CAAC;IACJ,CAAC;IAED,IAAI,gBAAgB,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,gBAAgB,GAAG,kBAAkB,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;QAChF,OAAO,CAAC,gBAAI,CAAC,AAAD,EAAG,CAAC;IAClB,CAAC;IAED,6EAA6E;IAC7E,OAAO,CACL,CAAC,oBAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,gBAAgB,GAAG,kBAAkB,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAC7E;MAAA,CAAC,cAAc,CACf;MAAA,CAAC,oBAAK,CAAC,MAAM,CACX;QAAA,CAAC,WAAW,CAAC,AAAD,EACd;MAAA,EAAE,oBAAK,CAAC,MAAM,CACd;MAAA,CAAC,iBAAiB,CACpB;IAAA,EAAE,oBAAK,CAAC,IAAI,CAAC,CACd,CAAC;AACJ,CAAC;AAEY,QAAA,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE;IACzD,MAAM,EAAE,0BAAe;IACvB,SAAS,EAAE,6BAAkB;CAC9B,CAAC,CAAC","sourcesContent":["import React, { createContext, isValidElement, use, type ReactNode } from 'react';\nimport { Split, type SplitHostProps } from 'react-native-screens/experimental';\n\nimport { SplitViewColumn, SplitViewInspector } from './elements';\nimport { IsWithinLayoutContext } from '../layouts/IsWithinLayoutContext';\nimport { Slot } from '../views/Navigator';\n\nconst IsWithinSplitViewContext = createContext(false);\n\n/**\n * For full list of supported props, see [`SplitHostProps`](http://github.com/software-mansion/react-native-screens/blob/main/src/components/gamma/split/SplitHost.types.ts#L117)\n */\nexport interface SplitViewProps extends Omit {\n children?: ReactNode;\n}\n\nfunction SplitViewNavigator({ children, ...splitViewHostProps }: SplitViewProps) {\n if (use(IsWithinSplitViewContext)) {\n throw new Error('There can only be one SplitView in the navigation hierarchy.');\n }\n\n // TODO: Add better way of detecting if SplitView is rendered inside Native navigator.\n if (use(IsWithinLayoutContext)) {\n throw new Error('SplitView cannot be used inside another navigator, except for Slot.');\n }\n\n if (process.env.EXPO_OS !== 'ios') {\n console.warn(\n 'SplitView is only supported on iOS. The SplitView will behave like a Slot navigator on other platforms.'\n );\n return ;\n }\n\n const WrappedSlot = () => (\n \n \n \n );\n\n const allChildrenArray = React.Children.toArray(children);\n const columnChildren = allChildrenArray.filter(\n (child) => isValidElement(child) && child.type === SplitViewColumn\n );\n const inspectorChildren = allChildrenArray.filter(\n (child) => isValidElement(child) && child.type === SplitViewInspector\n );\n const numberOfSidebars = columnChildren.length;\n const numberOfInspectors = inspectorChildren.length;\n\n if (allChildrenArray.length !== columnChildren.length + inspectorChildren.length) {\n console.warn(\n 'Only SplitView.Column and SplitView.Inspector components are allowed as direct children of SplitView.'\n );\n }\n\n if (numberOfSidebars > 2) {\n throw new Error('There can only be two SplitView.Column in the SplitView.');\n }\n\n if (numberOfSidebars + numberOfInspectors === 0) {\n console.warn('No SplitView.Column and SplitView.Inspector found in SplitView.');\n return ;\n }\n\n // The key is needed, because number of columns cannot be changed dynamically\n return (\n \n {columnChildren}\n \n \n \n {inspectorChildren}\n \n );\n}\n\nexport const SplitView = Object.assign(SplitViewNavigator, {\n Column: SplitViewColumn,\n Inspector: SplitViewInspector,\n});\n"]}
\ No newline at end of file
diff --git a/packages/expo-router/src/loaders/__tests__/utils.test.web.ts b/packages/expo-router/src/loaders/__tests__/utils.test.web.ts
new file mode 100644
index 00000000000000..417e3e3f75226e
--- /dev/null
+++ b/packages/expo-router/src/loaders/__tests__/utils.test.web.ts
@@ -0,0 +1,35 @@
+import { getLoaderModulePath } from '../utils';
+
+describe(getLoaderModulePath, () => {
+ it('converts root path to /_expo/loaders/index', () => {
+ expect(getLoaderModulePath('/')).toBe('/_expo/loaders/index');
+ });
+
+ it('converts paths without trailing slash', () => {
+ expect(getLoaderModulePath('/about')).toBe('/_expo/loaders/about');
+ });
+
+ it('strips trailing slashes', () => {
+ expect(getLoaderModulePath('/about/')).toBe('/_expo/loaders/about');
+ });
+
+ it('handles nested paths', () => {
+ expect(getLoaderModulePath('/posts/123')).toBe('/_expo/loaders/posts/123');
+ });
+
+ it('preserves query parameters', () => {
+ expect(getLoaderModulePath('/request?foo=bar')).toBe('/_expo/loaders/request?foo=bar');
+ });
+
+ it('preserves query parameters on root path', () => {
+ expect(getLoaderModulePath('/?foo=bar')).toBe('/_expo/loaders/index?foo=bar');
+ });
+
+ it('preserves multiple query parameters', () => {
+ expect(getLoaderModulePath('/request?a=1&b=2')).toBe('/_expo/loaders/request?a=1&b=2');
+ });
+
+ it('preserves query parameters with trailing slash', () => {
+ expect(getLoaderModulePath('/about/?foo=bar')).toBe('/_expo/loaders/about?foo=bar');
+ });
+});
diff --git a/packages/expo-router/src/loaders/utils.ts b/packages/expo-router/src/loaders/utils.ts
index 1b30e185f060d9..371b4151ad7aa9 100644
--- a/packages/expo-router/src/loaders/utils.ts
+++ b/packages/expo-router/src/loaders/utils.ts
@@ -8,20 +8,23 @@ import { parseUrlUsingCustomBase } from '../utils/url';
* getLoaderModulePath(`/about`) // `/_expo/loaders/about`
* getLoaderModulePath(`/posts/1`) // `/_expo/loaders/posts/1`
*/
-export function getLoaderModulePath(pathname: string): string {
- const urlPath = parseUrlUsingCustomBase(pathname).pathname;
- const normalizedPath = urlPath === '/' ? '/' : urlPath.replace(/\/$/, '');
+export function getLoaderModulePath(routePath: string): string {
+ const { pathname, search } = parseUrlUsingCustomBase(routePath);
+ const normalizedPath = pathname === '/' ? '/' : pathname.replace(/\/$/, '');
const pathSegment = normalizedPath === '/' ? '/index' : normalizedPath;
- return `/_expo/loaders${pathSegment}`;
+ return `/_expo/loaders${pathSegment}${search}`;
}
/**
* Fetches and parses a loader module from the given route path.
* This works in all environments including:
- * 1. Development with Metro dev server (see `LoaderModuleMiddleware`)
+ * 1. Development with Metro dev server
* 2. Production with static files (SSG)
* 3. SSR environments
+ *
+ * @see import('packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts').createRouteHandlerMiddleware
+ * @see import('packages/expo-server/src/vendor/environment/common.ts').createEnvironment
*/
export async function fetchLoaderModule(routePath: string): Promise {
const loaderPath = getLoaderModulePath(routePath);
diff --git a/packages/expo-router/src/native-tabs/NativeTabsView.tsx b/packages/expo-router/src/native-tabs/NativeTabsView.tsx
index 66d1de9d7b8104..daa80ea1d6b045 100644
--- a/packages/expo-router/src/native-tabs/NativeTabsView.tsx
+++ b/packages/expo-router/src/native-tabs/NativeTabsView.tsx
@@ -1,12 +1,7 @@
import { useTheme } from '@react-navigation/native';
import React, { useDeferredValue, useMemo } from 'react';
import { View, type ColorValue } from 'react-native';
-import {
- BottomTabs,
- BottomTabsScreen,
- type BottomTabsProps,
- type BottomTabsScreenAppearance,
-} from 'react-native-screens';
+import { Tabs, type TabsHostProps, type TabsScreenAppearance } from 'react-native-screens';
import { SafeAreaView } from 'react-native-screens/experimental';
import {
@@ -80,7 +75,7 @@ export function NativeTabsView(props: NativeTabsViewProps) {
});
const currentTabAppearance = appearances[inBoundsDeferredFocusedIndex]?.standardAppearance;
- const tabBarControllerMode: BottomTabsProps['tabBarControllerMode'] = sidebarAdaptable
+ const tabBarControllerMode: TabsHostProps['tabBarControllerMode'] = sidebarAdaptable
? 'tabSidebar'
: sidebarAdaptable === false
? 'tabBar'
@@ -100,7 +95,7 @@ export function NativeTabsView(props: NativeTabsViewProps) {
: undefined;
return (
-
{children}
-
+
);
}
@@ -157,8 +152,8 @@ function Screen(props: {
name: string;
isFocused: boolean;
options: NativeTabOptions;
- standardAppearance: BottomTabsScreenAppearance;
- scrollEdgeAppearance: BottomTabsScreenAppearance;
+ standardAppearance: TabsScreenAppearance;
+ scrollEdgeAppearance: TabsScreenAppearance;
badgeTextColor: ColorValue | undefined;
contentRenderer: () => React.ReactNode;
}) {
@@ -205,7 +200,7 @@ function Screen(props: {
);
return (
-
{wrappedContent}
-
+
);
}
@@ -232,7 +227,7 @@ const supportedTabBarItemLabelVisibilityModesSet = new Set(
SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES
);
-function BottomTabsWrapper(props: BottomTabsProps) {
+function TabsHostWrapper(props: TabsHostProps) {
let { tabBarMinimizeBehavior, tabBarItemLabelVisibilityMode, ...rest } = props;
if (tabBarMinimizeBehavior && !supportedTabBarMinimizeBehaviorsSet.has(tabBarMinimizeBehavior)) {
console.warn(
@@ -251,7 +246,7 @@ function BottomTabsWrapper(props: BottomTabsProps) {
}
return (
- {
const { View }: typeof import('react-native') = jest.requireActual('react-native');
+ const actualModule = jest.requireActual(
+ 'react-native-screens'
+ ) as typeof import('react-native-screens');
return {
- ...(jest.requireActual('react-native-screens') as typeof import('react-native-screens')),
- BottomTabs: jest.fn(({ children }) => {children}),
- BottomTabsScreen: jest.fn(({ children }) => {children}),
+ ...actualModule,
+ Tabs: {
+ ...actualModule.Tabs,
+ Host: jest.fn(({ children }) => {children}),
+ Screen: jest.fn(({ children }) => {children}),
+ },
};
});
-const BottomTabs = _BottomTabs as jest.MockedFunction;
-const BottomTabsScreen = _BottomTabsScreen as jest.MockedFunction;
+const TabsHost = Tabs.Host as jest.MockedFunction;
+const TabsScreen = Tabs.Screen as jest.MockedFunction;
it.each([
{ value: undefined, expected: 'automatic' },
@@ -32,7 +30,7 @@ it.each([
{ value: false, expected: 'tabBar' },
] as {
value: NativeTabsProps['sidebarAdaptable'];
- expected: BottomTabsProps['tabBarControllerMode'];
+ expected: TabsHostProps['tabBarControllerMode'];
}[])('when sidebarAdaptable is $value, then ', ({ value, expected }) => {
renderRouter({
_layout: () => (
@@ -44,8 +42,8 @@ it.each([
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0].tabBarControllerMode).toBe(expected);
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0].tabBarControllerMode).toBe(expected);
});
it('uses shadowColor when it is passed to NativeTabs', () => {
@@ -59,11 +57,9 @@ it('uses shadowColor when it is passed to NativeTabs', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].standardAppearance.tabBarShadowColor).toBe('red');
- expect(BottomTabsScreen.mock.calls[0][0].scrollEdgeAppearance.tabBarShadowColor).toBe(
- 'transparent'
- );
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].standardAppearance.tabBarShadowColor).toBe('red');
+ expect(TabsScreen.mock.calls[0][0].scrollEdgeAppearance.tabBarShadowColor).toBe('transparent');
});
it('uses shadowColor when it is passed to NativeTabs in both standardAppearance and scrollEdgeAppearance when disableTransparentOnScrollEdge is true', () => {
@@ -77,7 +73,7 @@ it('uses shadowColor when it is passed to NativeTabs in both standardAppearance
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].standardAppearance.tabBarShadowColor).toBe('red');
- expect(BottomTabsScreen.mock.calls[0][0].scrollEdgeAppearance.tabBarShadowColor).toBe('red');
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].standardAppearance.tabBarShadowColor).toBe('red');
+ expect(TabsScreen.mock.calls[0][0].scrollEdgeAppearance.tabBarShadowColor).toBe('red');
});
diff --git a/packages/expo-router/src/native-tabs/__tests__/appearance.test.ios.tsx b/packages/expo-router/src/native-tabs/__tests__/appearance.test.ios.tsx
index b9f0638ae745ea..feeee8c0d89faf 100644
--- a/packages/expo-router/src/native-tabs/__tests__/appearance.test.ios.tsx
+++ b/packages/expo-router/src/native-tabs/__tests__/appearance.test.ios.tsx
@@ -1,7 +1,7 @@
import type {
- BottomTabsScreenAppearance,
- BottomTabsScreenItemAppearance,
- BottomTabsScreenItemStateAppearance,
+ TabsScreenAppearance,
+ TabsScreenItemAppearance,
+ TabsScreenItemStateAppearance,
} from 'react-native-screens';
import {
@@ -18,13 +18,13 @@ describe(createStandardAppearanceFromOptions, () => {
it('empty options should create empty appearance', () => {
const options: NativeTabOptions = {};
const result = createStandardAppearanceFromOptions(options);
- const expectedItemAppearance: BottomTabsScreenItemAppearance = {
+ const expectedItemAppearance: TabsScreenItemAppearance = {
normal: {},
selected: {},
disabled: {},
focused: {},
};
- const expectedAppearance: BottomTabsScreenAppearance = {
+ const expectedAppearance: TabsScreenAppearance = {
stacked: expectedItemAppearance,
inline: expectedItemAppearance,
compactInline: expectedItemAppearance,
@@ -53,7 +53,7 @@ describe(createStandardAppearanceFromOptions, () => {
const result = createStandardAppearanceFromOptions(options);
- const expectedItemAppearance: BottomTabsScreenItemAppearance = {
+ const expectedItemAppearance: TabsScreenItemAppearance = {
normal: {
tabBarItemBadgeBackgroundColor: 'green',
tabBarItemIconColor: 'red',
@@ -74,7 +74,7 @@ describe(createStandardAppearanceFromOptions, () => {
},
disabled: {},
};
- const expectedAppearance: BottomTabsScreenAppearance = {
+ const expectedAppearance: TabsScreenAppearance = {
stacked: expectedItemAppearance,
inline: expectedItemAppearance,
compactInline: expectedItemAppearance,
@@ -92,13 +92,13 @@ describe(createScrollEdgeAppearanceFromOptions, () => {
const options: NativeTabOptions = {};
const baseAppearance = {};
const result = createScrollEdgeAppearanceFromOptions(options, baseAppearance);
- const expectedItemAppearance: BottomTabsScreenItemAppearance = {
+ const expectedItemAppearance: TabsScreenItemAppearance = {
normal: {},
selected: {},
disabled: {},
focused: {},
};
- const expectedAppearance: BottomTabsScreenAppearance = {
+ const expectedAppearance: TabsScreenAppearance = {
stacked: expectedItemAppearance,
inline: expectedItemAppearance,
compactInline: expectedItemAppearance,
@@ -129,7 +129,7 @@ describe(createScrollEdgeAppearanceFromOptions, () => {
};
const result = createScrollEdgeAppearanceFromOptions(options);
- const expectedItemAppearance: BottomTabsScreenItemAppearance = {
+ const expectedItemAppearance: TabsScreenItemAppearance = {
normal: {
tabBarItemBadgeBackgroundColor: 'green',
tabBarItemIconColor: 'red',
@@ -150,7 +150,7 @@ describe(createScrollEdgeAppearanceFromOptions, () => {
},
disabled: {},
};
- const expectedAppearance: BottomTabsScreenAppearance = {
+ const expectedAppearance: TabsScreenAppearance = {
stacked: expectedItemAppearance,
inline: expectedItemAppearance,
compactInline: expectedItemAppearance,
@@ -168,7 +168,7 @@ describe(appendStyleToAppearance, () => {
[['normal']],
[['normal', 'focused']],
[['normal', 'focused', 'selected']],
- ] as (keyof BottomTabsScreenItemAppearance)[][][])('for states %p', (states) => {
+ ] as (keyof TabsScreenItemAppearance)[][][])('for states %p', (states) => {
it.each([
[
{
@@ -214,15 +214,12 @@ describe(appendStyleToAppearance, () => {
},
},
],
- ] as [BottomTabsScreenAppearance][])(
- 'empty style should not change appearance %p',
- (appearance) => {
- const result = appendStyleToAppearance({}, appearance, states);
- expect(result).toEqual(appearance);
- }
- );
+ ] as [TabsScreenAppearance][])('empty style should not change appearance %p', (appearance) => {
+ const result = appendStyleToAppearance({}, appearance, states);
+ expect(result).toEqual(appearance);
+ });
it('should append style correctly', () => {
- const item: BottomTabsScreenItemAppearance = {
+ const item: TabsScreenItemAppearance = {
normal: {
tabBarItemIconColor: '#f00',
tabBarItemBadgeBackgroundColor: '#0f0',
@@ -234,7 +231,7 @@ describe(appendStyleToAppearance, () => {
focused: {},
disabled: {},
};
- const appearance: BottomTabsScreenAppearance = {
+ const appearance: TabsScreenAppearance = {
stacked: item,
inline: item,
compactInline: item,
@@ -273,7 +270,7 @@ describe(appendStyleToAppearance, () => {
...newStateAppearance,
}
: item.focused;
- const expectedItem: BottomTabsScreenItemAppearance = {
+ const expectedItem: TabsScreenItemAppearance = {
normal,
selected,
focused,
@@ -304,7 +301,7 @@ describe(appendStyleToAppearance, () => {
const normal = states.includes('normal') ? newStateAppearance : {};
const selected = states.includes('selected') ? newStateAppearance : {};
const focused = states.includes('focused') ? newStateAppearance : {};
- const expectedItem: BottomTabsScreenItemAppearance = {
+ const expectedItem: TabsScreenItemAppearance = {
normal,
selected,
focused,
@@ -327,7 +324,7 @@ describe(convertStyleToAppearance, () => {
blurEffect: 'light',
fontFamily: 'Arial',
};
- const expected: BottomTabsScreenItemStateAppearance = {
+ const expected: TabsScreenItemStateAppearance = {
tabBarItemTitleFontFamily: 'Arial',
};
const result = convertStyleToAppearance(style);
@@ -345,7 +342,7 @@ describe(convertStyleToAppearance, () => {
tabBarBlurEffect: style.blurEffect,
});
});
- const cases: [AppearanceStyle, BottomTabsScreenItemStateAppearance][] = [
+ const cases: [AppearanceStyle, TabsScreenItemStateAppearance][] = [
[{}, {}],
[{ fontFamily: 'xxx' }, { tabBarItemTitleFontFamily: 'xxx' }],
[{ fontSize: 16 }, { tabBarItemTitleFontSize: 16 }],
@@ -424,7 +421,7 @@ describe(convertStyleToAppearance, () => {
});
});
describe(convertStyleToItemStateAppearance, () => {
- const cases: [AppearanceStyle, BottomTabsScreenItemStateAppearance][] = [
+ const cases: [AppearanceStyle, TabsScreenItemStateAppearance][] = [
[{}, {}],
[{ backgroundColor: '#fff' }, {}],
[{ blurEffect: 'none' }, {}],
diff --git a/packages/expo-router/src/native-tabs/__tests__/events.test.ios.tsx b/packages/expo-router/src/native-tabs/__tests__/events.test.ios.tsx
index 3272de0c6f0704..650a08d91d77fd 100644
--- a/packages/expo-router/src/native-tabs/__tests__/events.test.ios.tsx
+++ b/packages/expo-router/src/native-tabs/__tests__/events.test.ios.tsx
@@ -2,9 +2,8 @@ import { screen } from '@testing-library/react-native';
import React from 'react';
import { View, type NativeSyntheticEvent } from 'react-native';
import {
- BottomTabsScreen as _BottomTabsScreen,
- BottomTabs as _BottomTabs,
- type BottomTabsProps,
+ Tabs,
+ type TabsHostProps,
// @ts-expect-error: method is declared in mock below
__triggerNativeFocusChange,
} from 'react-native-screens';
@@ -17,25 +16,31 @@ import { NativeTabs } from '../NativeTabs';
jest.mock('react-native-screens', () => {
const { View }: typeof import('react-native') = jest.requireActual('react-native');
- let triggerNativeFocusChange: BottomTabsProps['onNativeFocusChange'] = () => {};
+ const actualModule = jest.requireActual(
+ 'react-native-screens'
+ ) as typeof import('react-native-screens');
+ let triggerNativeFocusChange: TabsHostProps['onNativeFocusChange'] = () => {};
return {
- ...(jest.requireActual('react-native-screens') as typeof import('react-native-screens')),
- BottomTabs: jest.fn(({ children, onNativeFocusChange }) => {
- triggerNativeFocusChange = onNativeFocusChange || (() => {});
- return {children};
- }),
- BottomTabsScreen: jest.fn(({ children }) => {children}),
- __triggerNativeFocusChange: (event: Parameters[0]) =>
+ ...actualModule,
+ Tabs: {
+ ...actualModule.Tabs,
+ Host: jest.fn(({ children, onNativeFocusChange }) => {
+ triggerNativeFocusChange = onNativeFocusChange || (() => {});
+ return {children};
+ }),
+ Screen: jest.fn(({ children }) => {children}),
+ },
+ __triggerNativeFocusChange: (event: Parameters[0]) =>
triggerNativeFocusChange(event),
};
});
-const triggerNativeFocusChange: BottomTabsProps['onNativeFocusChange'] = (...args) =>
+const triggerNativeFocusChange: TabsHostProps['onNativeFocusChange'] = (...args) =>
act(() => {
__triggerNativeFocusChange(...args);
});
-const BottomTabsScreen = _BottomTabsScreen as jest.MockedFunction;
+const TabsScreen = Tabs.Screen as jest.MockedFunction;
const warn = jest.fn();
const error = jest.fn();
@@ -88,12 +93,12 @@ it('emits tabPress event onNativeFocusChange', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].tabKey).toMatch(/index-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[1][0].tabKey).toMatch(/second-[-\w]+/);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].tabKey).toMatch(/index-[-\w]+/);
+ expect(TabsScreen.mock.calls[1][0].tabKey).toMatch(/second-[-\w]+/);
- const indexTabKey = BottomTabsScreen.mock.calls[0][0].tabKey;
- const secondTabKey = BottomTabsScreen.mock.calls[1][0].tabKey;
+ const indexTabKey = TabsScreen.mock.calls[0][0].tabKey;
+ const secondTabKey = TabsScreen.mock.calls[1][0].tabKey;
expect(indexTabPressHandler).toHaveBeenCalledTimes(0);
expect(secondTabPressHandler).toHaveBeenCalledTimes(0);
@@ -101,8 +106,9 @@ it('emits tabPress event onNativeFocusChange', () => {
triggerNativeFocusChange({
nativeEvent: {
tabKey: indexTabKey,
+ repeatedSelectionHandledBySpecialEffect: false,
},
- } as NativeSyntheticEvent<{ tabKey: string }>);
+ } as NativeSyntheticEvent<{ tabKey: string; repeatedSelectionHandledBySpecialEffect: boolean }>);
act(() => jest.runAllTimers());
@@ -114,8 +120,9 @@ it('emits tabPress event onNativeFocusChange', () => {
triggerNativeFocusChange({
nativeEvent: {
tabKey: secondTabKey,
+ repeatedSelectionHandledBySpecialEffect: false,
},
- } as NativeSyntheticEvent<{ tabKey: string }>);
+ } as NativeSyntheticEvent<{ tabKey: string; repeatedSelectionHandledBySpecialEffect: boolean }>);
act(() => jest.runAllTimers());
@@ -127,8 +134,9 @@ it('emits tabPress event onNativeFocusChange', () => {
triggerNativeFocusChange({
nativeEvent: {
tabKey: secondTabKey,
+ repeatedSelectionHandledBySpecialEffect: false,
},
- } as NativeSyntheticEvent<{ tabKey: string }>);
+ } as NativeSyntheticEvent<{ tabKey: string; repeatedSelectionHandledBySpecialEffect: boolean }>);
act(() => jest.runAllTimers());
@@ -183,12 +191,12 @@ it('does not pop stack on repeated tab press', async () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('a-index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].tabKey).toMatch(/index-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[1][0].tabKey).toMatch(/a-[-\w]+/);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].tabKey).toMatch(/index-[-\w]+/);
+ expect(TabsScreen.mock.calls[1][0].tabKey).toMatch(/a-[-\w]+/);
- const indexTabKey = BottomTabsScreen.mock.calls[0][0].tabKey;
- const aTabKey = BottomTabsScreen.mock.calls[1][0].tabKey;
+ const indexTabKey = TabsScreen.mock.calls[0][0].tabKey;
+ const aTabKey = TabsScreen.mock.calls[1][0].tabKey;
expect(indexTabPressHandler).toHaveBeenCalledTimes(0);
expect(aIndexTabPressHandler).toHaveBeenCalledTimes(0);
@@ -197,8 +205,9 @@ it('does not pop stack on repeated tab press', async () => {
triggerNativeFocusChange({
nativeEvent: {
tabKey: indexTabKey,
+ repeatedSelectionHandledBySpecialEffect: false,
},
- } as NativeSyntheticEvent<{ tabKey: string }>);
+ } as NativeSyntheticEvent<{ tabKey: string; repeatedSelectionHandledBySpecialEffect: boolean }>);
act(() => jest.runAllTimers());
@@ -210,8 +219,9 @@ it('does not pop stack on repeated tab press', async () => {
triggerNativeFocusChange({
nativeEvent: {
tabKey: aTabKey,
+ repeatedSelectionHandledBySpecialEffect: false,
},
- } as NativeSyntheticEvent<{ tabKey: string }>);
+ } as NativeSyntheticEvent<{ tabKey: string; repeatedSelectionHandledBySpecialEffect: boolean }>);
act(() => jest.runAllTimers());
@@ -231,8 +241,9 @@ it('does not pop stack on repeated tab press', async () => {
triggerNativeFocusChange({
nativeEvent: {
tabKey: aTabKey,
+ repeatedSelectionHandledBySpecialEffect: true,
},
- } as NativeSyntheticEvent<{ tabKey: string }>);
+ } as NativeSyntheticEvent<{ tabKey: string; repeatedSelectionHandledBySpecialEffect: boolean }>);
act(() => jest.runAllTimers());
diff --git a/packages/expo-router/src/native-tabs/__tests__/navigation.test.ios.tsx b/packages/expo-router/src/native-tabs/__tests__/navigation.test.ios.tsx
index fd219d90b17c52..3b769f8c24ac1a 100644
--- a/packages/expo-router/src/native-tabs/__tests__/navigation.test.ios.tsx
+++ b/packages/expo-router/src/native-tabs/__tests__/navigation.test.ios.tsx
@@ -1,7 +1,7 @@
import { screen, fireEvent } from '@testing-library/react-native';
-import React, { act } from 'react';
+import { act } from 'react';
import { View } from 'react-native';
-import { BottomTabsScreen as _BottomTabsScreen } from 'react-native-screens';
+import { Tabs } from 'react-native-screens';
import { router } from '../../imperative-api';
import { Link } from '../../link/Link';
@@ -10,44 +10,46 @@ import { NativeTabs } from '../NativeTabs';
jest.mock('react-native-screens', () => {
const { View }: typeof import('react-native') = jest.requireActual('react-native');
+ const actualModule = jest.requireActual(
+ 'react-native-screens'
+ ) as typeof import('react-native-screens');
return {
- ...(jest.requireActual('react-native-screens') as typeof import('react-native-screens')),
- BottomTabs: jest.fn(({ children }) => {children}),
- BottomTabsScreen: jest.fn(({ children }) => {children}),
+ ...actualModule,
+ Tabs: {
+ ...actualModule.Tabs,
+ Host: jest.fn(({ children }) => {children}),
+ Screen: jest.fn(({ children }) => {children}),
+ },
};
});
-const BottomTabsScreen = _BottomTabsScreen as jest.MockedFunction;
+const TabsScreen = Tabs.Screen as jest.MockedFunction;
describe('Native Bottom Tabs Navigation', () => {
function expectNoRenders() {
- expect(BottomTabsScreen).not.toHaveBeenCalled();
+ expect(TabsScreen).not.toHaveBeenCalled();
}
function expectOneRender() {
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
}
function expectTwoRenders() {
- expect(BottomTabsScreen).toHaveBeenCalledTimes(4);
+ expect(TabsScreen).toHaveBeenCalledTimes(4);
}
function expectIndexTabFocused(renderNumber = 1) {
- expect(BottomTabsScreen.mock.calls[(renderNumber - 1) * 2][0].tabKey).toMatch(/^index-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[(renderNumber - 1) * 2][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[(renderNumber - 1) * 2 + 1][0].tabKey).toMatch(
- /^second-[-\w]+/
- );
- expect(BottomTabsScreen.mock.calls[(renderNumber - 1) * 2 + 1][0].isFocused).toBe(false);
+ expect(TabsScreen.mock.calls[(renderNumber - 1) * 2][0].tabKey).toMatch(/^index-[-\w]+/);
+ expect(TabsScreen.mock.calls[(renderNumber - 1) * 2][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[(renderNumber - 1) * 2 + 1][0].tabKey).toMatch(/^second-[-\w]+/);
+ expect(TabsScreen.mock.calls[(renderNumber - 1) * 2 + 1][0].isFocused).toBe(false);
}
function expectSecondTabFocused(renderNumber = 1) {
- expect(BottomTabsScreen.mock.calls[(renderNumber - 1) * 2][0].tabKey).toMatch(/^index-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[(renderNumber - 1) * 2][0].isFocused).toBe(false);
- expect(BottomTabsScreen.mock.calls[(renderNumber - 1) * 2 + 1][0].tabKey).toMatch(
- /^second-[-\w]+/
- );
- expect(BottomTabsScreen.mock.calls[(renderNumber - 1) * 2 + 1][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[(renderNumber - 1) * 2][0].tabKey).toMatch(/^index-[-\w]+/);
+ expect(TabsScreen.mock.calls[(renderNumber - 1) * 2][0].isFocused).toBe(false);
+ expect(TabsScreen.mock.calls[(renderNumber - 1) * 2 + 1][0].tabKey).toMatch(/^second-[-\w]+/);
+ expect(TabsScreen.mock.calls[(renderNumber - 1) * 2 + 1][0].isFocused).toBe(true);
}
beforeEach(() => {
@@ -80,14 +82,14 @@ describe('Native Bottom Tabs Navigation', () => {
});
expectOneRender();
expectIndexTabFocused();
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
});
it('can navigate using router.push', () => {
act(() => router.push('/second'));
expectTwoRenders();
expectSecondTabFocused(2);
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => router.push('/'));
expectTwoRenders();
expectIndexTabFocused(2);
@@ -100,7 +102,7 @@ describe('Native Bottom Tabs Navigation', () => {
// Second one is deferred index=1, index =1
expectTwoRenders();
expectSecondTabFocused(2);
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => fireEvent.press(screen.getByTestId('second-index-link')));
expectTwoRenders();
expectIndexTabFocused(2);
@@ -117,11 +119,11 @@ describe('Native Bottom Tabs Navigation', () => {
expectOneRender();
expectIndexTabFocused();
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => router.push('/second'));
expectSecondTabFocused(2);
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => fireEvent.press(screen.getByTestId('second-second-link'))); // link to same tab
expectOneRender();
expectSecondTabFocused();
@@ -131,11 +133,11 @@ describe('Native Bottom Tabs Navigation', () => {
act(() => fireEvent.press(screen.getByTestId('index-hidden-link')));
expectNoRenders();
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => router.push('/second'));
expectSecondTabFocused(2);
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => fireEvent.press(screen.getByTestId('second-hidden-link')));
expectNoRenders();
});
@@ -144,11 +146,11 @@ describe('Native Bottom Tabs Navigation', () => {
act(() => fireEvent.press(screen.getByTestId('index-not-specified-link')));
expectNoRenders();
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => router.push('/second'));
expectSecondTabFocused();
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => fireEvent.press(screen.getByTestId('second-hidden-link')));
expectNoRenders();
});
diff --git a/packages/expo-router/src/native-tabs/__tests__/options.e2e.test.android.tsx b/packages/expo-router/src/native-tabs/__tests__/options.e2e.test.android.tsx
index f2872774149030..a441da5db2d91e 100644
--- a/packages/expo-router/src/native-tabs/__tests__/options.e2e.test.android.tsx
+++ b/packages/expo-router/src/native-tabs/__tests__/options.e2e.test.android.tsx
@@ -1,12 +1,7 @@
import { screen, act, fireEvent, waitFor } from '@testing-library/react-native';
import { Children, isValidElement, useState, type ComponentProps, type ReactNode } from 'react';
import { Button, Text, View } from 'react-native';
-import {
- BottomTabs as _BottomTabs,
- BottomTabsScreen as _BottomTabsScreen,
- type BottomTabsProps,
- type BottomTabsScreenProps,
-} from 'react-native-screens';
+import { Tabs, type TabsHostProps, type TabsScreenProps } from 'react-native-screens';
import { SafeAreaView } from 'react-native-screens/experimental';
import type { ColorType } from '../../color';
@@ -38,10 +33,16 @@ jest.mock('../../color', (): typeof import('../../color') => ({
jest.mock('react-native-screens', () => {
const { View }: typeof import('react-native') = jest.requireActual('react-native');
+ const actualModule = jest.requireActual(
+ 'react-native-screens'
+ ) as typeof import('react-native-screens');
return {
- ...(jest.requireActual('react-native-screens') as typeof import('react-native-screens')),
- BottomTabs: jest.fn(({ children }) => {children}),
- BottomTabsScreen: jest.fn(({ children }) => {children}),
+ ...actualModule,
+ Tabs: {
+ ...actualModule.Tabs,
+ Host: jest.fn(({ children }) => {children}),
+ Screen: jest.fn(({ children }) => {children}),
+ },
};
});
jest.mock('react-native-screens/experimental', () => {
@@ -57,8 +58,8 @@ jest.mock('react-native-screens/experimental', () => {
};
});
-const BottomTabs = _BottomTabs as jest.MockedFunction;
-const BottomTabsScreen = _BottomTabsScreen as jest.MockedFunction;
+const TabsHost = Tabs.Host as jest.MockedFunction;
+const TabsScreen = Tabs.Screen as jest.MockedFunction;
it('can pass props via unstable_nativeProps', () => {
const indexOptions = {
@@ -82,11 +83,11 @@ it('can pass props via unstable_nativeProps', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
...indexOptions,
});
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
...secondOptions,
});
});
@@ -104,15 +105,14 @@ it('can pass options via elements', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
icon: { android: { type: 'drawableResource', name: 'test' } },
selectedIcon: undefined,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when no options are passed, default ones are used', () => {
- const { BottomTabsScreen } = require('react-native-screens');
renderRouter({
_layout: () => (
@@ -123,8 +123,8 @@ it('when no options are passed, default ones are used', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
hidden: false,
specialEffects: {},
tabKey: expect.stringMatching(/^index-[-\w]+/),
@@ -133,7 +133,7 @@ it('when no options are passed, default ones are used', () => {
icon: undefined,
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
describe('Icons', () => {
@@ -157,10 +157,10 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
icon: { android: { type: 'drawableResource', name: 'homepod' } },
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
expect(consoleWarnMock).not.toHaveBeenCalled();
});
@@ -177,9 +177,9 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].icon).toBeUndefined();
- expect(BottomTabsScreen.mock.calls[0][0].selectedIcon).toBeUndefined();
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].icon).toBeUndefined();
+ expect(TabsScreen.mock.calls[0][0].selectedIcon).toBeUndefined();
expect(consoleWarnMock).not.toHaveBeenCalled();
});
@@ -197,11 +197,11 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
icon: { android: { type: 'drawableResource', name: 'homepod' } },
selectedIcon: undefined,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
expect(consoleWarnMock).not.toHaveBeenCalled();
});
@@ -215,8 +215,8 @@ describe('Icons', () => {
index: () => ,
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -224,7 +224,7 @@ describe('Icons', () => {
},
},
},
- } as Partial);
+ } as Partial);
expect(consoleWarnMock).not.toHaveBeenCalled();
});
@@ -242,8 +242,8 @@ describe('Icons', () => {
one: () => ,
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -251,8 +251,8 @@ describe('Icons', () => {
},
},
},
- } as Partial);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as Partial);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -260,7 +260,7 @@ describe('Icons', () => {
},
},
},
- } as Partial);
+ } as Partial);
expect(consoleWarnMock).not.toHaveBeenCalled();
});
@@ -294,7 +294,7 @@ describe('Icons', () => {
] as (DrawableIcon &
SrcIcon &
SFSymbolIcon & {
- expectedIcon: BottomTabsScreenProps['icon']['android'];
+ expectedIcon: TabsScreenProps['icon']['android'];
})[])(
'For , icon is $expectedIcon',
({ sf, src, drawable, expectedIcon }) => {
@@ -310,8 +310,8 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].icon?.android).toEqual(expectedIcon);
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].icon?.android).toEqual(expectedIcon);
expect(consoleWarnMock).not.toHaveBeenCalled();
}
);
@@ -344,9 +344,9 @@ describe('Icons', () => {
await waitFor(() => {
expect(screen.getByTestId('index')).toBeVisible();
});
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].icon?.android).toBeUndefined();
- expect(BottomTabsScreen.mock.calls[1][0].icon?.android).toEqual({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].icon?.android).toBeUndefined();
+ expect(TabsScreen.mock.calls[1][0].icon?.android).toEqual({
// This is declared in packages/expo-font/mocks/ExpoFontUtils.ts
imageSource: { height: 0, uri: '', width: 0, scale: 1 },
type: 'imageSource',
@@ -371,7 +371,7 @@ describe('Icons', () => {
},
] as (MaterialIcon &
DrawableIcon & {
- expectedIcon: BottomTabsScreenProps['icon']['android'];
+ expectedIcon: TabsScreenProps['icon']['android'];
numberOfRenders: number;
})[])(
'For , icon will be $expectedIcon during first render and tabs will render $numberOfRenders time(s)',
@@ -393,10 +393,10 @@ describe('Icons', () => {
await waitFor(() => {
expect(screen.getByTestId('index')).toBeVisible();
});
- expect(BottomTabsScreen).toHaveBeenCalledTimes(numberOfRenders);
- expect(BottomTabsScreen.mock.calls[0][0].icon?.android).toEqual(expectedIcon);
+ expect(TabsScreen).toHaveBeenCalledTimes(numberOfRenders);
+ expect(TabsScreen.mock.calls[0][0].icon?.android).toEqual(expectedIcon);
if (numberOfRenders > 1) {
- expect(BottomTabsScreen.mock.calls[1][0].icon?.android).toEqual({
+ expect(TabsScreen.mock.calls[1][0].icon?.android).toEqual({
// This is declared in packages/expo-font/mocks/ExpoFontUtils.ts
imageSource: { height: 0, uri: '', width: 0, scale: 1 },
type: 'imageSource',
@@ -421,10 +421,10 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
badgeValue: '5',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('passes badge value as string', () => {
@@ -440,10 +440,10 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
badgeValue: 'New',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('does not pass badge when Badge is not used', () => {
@@ -457,8 +457,8 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).not.toHaveProperty('badgeValue');
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).not.toHaveProperty('badgeValue');
});
it('uses last Badge value when multiple are provided', () => {
@@ -476,10 +476,10 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
badgeValue: '3',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when empty Badge is used, passes space to badgeValue', () => {
@@ -495,8 +495,8 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].badgeValue).toBe(' '); // Space is used to show empty badge
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].badgeValue).toBe(' '); // Space is used to show empty badge
});
it('when empty Badge is used with hidden, passes undefined to badgeValue', () => {
@@ -512,8 +512,8 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].badgeValue).toBeUndefined();
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].badgeValue).toBeUndefined();
});
});
@@ -531,10 +531,10 @@ describe('Label', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Custom Title',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when title is not set, uses the route name', () => {
@@ -551,9 +551,9 @@ describe('Label', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('one')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].title).toBe('index');
- expect(BottomTabsScreen.mock.calls[1][0].title).toBe('one');
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].title).toBe('index');
+ expect(TabsScreen.mock.calls[1][0].title).toBe('one');
});
it('uses last Label value when multiple are provided', () => {
@@ -571,10 +571,10 @@ describe('Label', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Last Title',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when empty Label is used, passes route name to title', () => {
@@ -590,8 +590,8 @@ describe('Label', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].title).toBe('index'); // Route name is used as title when Label is empty
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].title).toBe('index'); // Route name is used as title when Label is empty
});
it('when Label with hidden is used, passes empty string to title', () => {
@@ -607,8 +607,8 @@ describe('Label', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].title).toBe(''); // Route name is used as title when Label is empty
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].title).toBe(''); // Route name is used as title when Label is empty
});
it('when selectedLabelStyle is provided, it is passed to screen', () => {
@@ -621,8 +621,8 @@ describe('Label', () => {
index: () => ,
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -631,7 +631,7 @@ describe('Label', () => {
},
},
},
- } as Partial);
+ } as Partial);
});
it('when selectedLabelStyle is provided in container and tab, the tab should use the tab color', () => {
@@ -648,8 +648,8 @@ describe('Label', () => {
one: () => ,
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -658,8 +658,8 @@ describe('Label', () => {
},
},
},
- } as Partial);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as Partial);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -668,7 +668,7 @@ describe('Label', () => {
},
},
},
- } as Partial);
+ } as Partial);
});
});
@@ -687,14 +687,14 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
specialEffects: {
repeatedTabSelection: {
popToRoot: false,
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('When disablePopToTop is not set or false, popToRoot is true', () => {
@@ -714,23 +714,23 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Custom Title',
specialEffects: {
repeatedTabSelection: {
popToRoot: true,
},
},
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'One',
specialEffects: {
repeatedTabSelection: {
popToRoot: true,
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
});
@@ -748,14 +748,14 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
specialEffects: {
repeatedTabSelection: {
scrollToTop: false,
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('When disableScrollToTop is not set or false, scrollToTop is true', () => {
@@ -775,23 +775,23 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Custom Title',
specialEffects: {
repeatedTabSelection: {
scrollToTop: true,
},
},
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'One',
specialEffects: {
repeatedTabSelection: {
scrollToTop: true,
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
});
@@ -808,8 +808,8 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- const children = BottomTabsScreen.mock.calls[0][0].children;
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ const children = TabsScreen.mock.calls[0][0].children;
expect(isValidElement(children)).toBe(true);
// Type assertion to satisfy TypeScript
if (!isValidElement(children)) throw new Error();
@@ -831,8 +831,8 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- const children = BottomTabsScreen.mock.calls[0][0].children;
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ const children = TabsScreen.mock.calls[0][0].children;
expect(Children.count(children)).toBe(1);
expect(isValidElement(children)).toBe(true);
// Type assertion to satisfy TypeScript
@@ -856,8 +856,8 @@ describe('Dynamic options', () => {
),
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
hidden: false,
specialEffects: {},
@@ -867,8 +867,8 @@ describe('Dynamic options', () => {
freezeContents: false,
icon: undefined,
selectedIcon: undefined,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Updated Title',
hidden: false,
specialEffects: {},
@@ -878,7 +878,7 @@ describe('Dynamic options', () => {
freezeContents: false,
icon: undefined,
selectedIcon: undefined,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('unstable_nativeProps override dynamic options configuration', () => {
@@ -899,8 +899,8 @@ describe('Dynamic options', () => {
),
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
hidden: false,
specialEffects: {},
@@ -909,8 +909,8 @@ describe('Dynamic options', () => {
icon: undefined,
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Initial Title',
hidden: false,
specialEffects: {},
@@ -925,7 +925,7 @@ describe('Dynamic options', () => {
},
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('can override component children from _layout with unstable_nativeProps', () => {
@@ -955,8 +955,8 @@ describe('Dynamic options', () => {
),
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
badgeValue: '3',
icon: {
@@ -969,8 +969,8 @@ describe('Dynamic options', () => {
specialEffects: {},
tabKey: expect.stringMatching(/^index-[-\w]+/),
isFocused: true,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Updated Title',
hidden: false,
specialEffects: {},
@@ -983,7 +983,7 @@ describe('Dynamic options', () => {
name: '1234',
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('can override component children from _layout with dynamic children', () => {
@@ -1008,8 +1008,8 @@ describe('Dynamic options', () => {
),
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
badgeValue: '3',
icon: {
@@ -1022,8 +1022,8 @@ describe('Dynamic options', () => {
specialEffects: {},
tabKey: expect.stringMatching(/^index-[-\w]+/),
isFocused: true,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Updated Title',
hidden: false,
specialEffects: {},
@@ -1036,7 +1036,7 @@ describe('Dynamic options', () => {
name: '1234',
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('can dynamically update options with state update', () => {
@@ -1062,15 +1062,15 @@ describe('Dynamic options', () => {
},
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].title).toBe('Initial Title');
- expect(BottomTabsScreen.mock.calls[1][0].title).toBe('Updated Title 0');
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].title).toBe('Initial Title');
+ expect(TabsScreen.mock.calls[1][0].title).toBe('Updated Title 0');
act(() => fireEvent.press(screen.getByTestId('update-button')));
- expect(BottomTabsScreen).toHaveBeenCalledTimes(3);
- expect(BottomTabsScreen.mock.calls[2][0].title).toBe('Updated Title 1');
+ expect(TabsScreen).toHaveBeenCalledTimes(3);
+ expect(TabsScreen.mock.calls[2][0].title).toBe('Updated Title 1');
act(() => fireEvent.press(screen.getByTestId('update-button')));
- expect(BottomTabsScreen).toHaveBeenCalledTimes(4);
- expect(BottomTabsScreen.mock.calls[3][0].title).toBe('Updated Title 2');
+ expect(TabsScreen).toHaveBeenCalledTimes(4);
+ expect(TabsScreen.mock.calls[3][0].title).toBe('Updated Title 2');
});
it('can be used in preview', () => {
@@ -1104,8 +1104,8 @@ describe('Dynamic options', () => {
// Tab + preview
expect(screen.getAllByTestId('second')).toHaveLength(2);
expect(within(screen.getByTestId('index')).getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
hidden: false,
specialEffects: {},
@@ -1114,8 +1114,8 @@ describe('Dynamic options', () => {
icon: undefined,
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Second',
hidden: false,
specialEffects: {},
@@ -1124,7 +1124,7 @@ describe('Dynamic options', () => {
icon: undefined,
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
});
@@ -1140,8 +1140,8 @@ describe('Material Design 3 dynamic color defaults', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0]).toMatchObject({
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0]).toMatchObject({
tabBarItemIconColor: 'mock-onSurfaceVariant',
tabBarItemTitleFontColor: 'mock-onSurfaceVariant',
tabBarItemIconColorActive: 'mock-onSecondaryContainer',
@@ -1149,7 +1149,7 @@ describe('Material Design 3 dynamic color defaults', () => {
tabBarBackgroundColor: 'mock-surfaceContainer',
tabBarItemRippleColor: 'mock-primary',
tabBarItemActiveIndicatorColor: 'mock-secondaryContainer',
- } as Partial);
+ } as Partial);
});
it('uses custom tintColor when provided, overriding Material defaults for active states', () => {
@@ -1163,15 +1163,15 @@ describe('Material Design 3 dynamic color defaults', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0]).toMatchObject({
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0]).toMatchObject({
tabBarItemIconColor: 'mock-onSurfaceVariant',
tabBarItemTitleFontColor: 'mock-onSurfaceVariant',
tabBarItemIconColorActive: 'red',
tabBarItemTitleFontColorActive: 'red',
tabBarBackgroundColor: 'mock-surfaceContainer',
tabBarItemRippleColor: 'mock-primary',
- } as Partial);
+ } as Partial);
});
it('uses custom rippleColor when provided, overriding Material default', () => {
@@ -1185,11 +1185,11 @@ describe('Material Design 3 dynamic color defaults', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0]).toMatchObject({
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0]).toMatchObject({
tabBarItemRippleColor: 'blue',
tabBarBackgroundColor: 'mock-surfaceContainer',
- } as Partial);
+ } as Partial);
});
it('uses appearance options when provided, overriding Material defaults', () => {
@@ -1206,14 +1206,14 @@ describe('Material Design 3 dynamic color defaults', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0]).toMatchObject({
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0]).toMatchObject({
tabBarItemIconColor: 'green',
tabBarItemIconColorActive: 'yellow',
tabBarItemTitleFontColor: 'purple',
tabBarItemTitleFontColorActive: 'orange',
tabBarBackgroundColor: 'pink',
- } as Partial);
+ } as Partial);
});
it('uses container indicatorColor when provided, overriding Material default', () => {
@@ -1227,10 +1227,10 @@ describe('Material Design 3 dynamic color defaults', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0]).toMatchObject({
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0]).toMatchObject({
tabBarItemActiveIndicatorColor: 'cyan',
tabBarBackgroundColor: 'mock-surfaceContainer',
- } as Partial);
+ } as Partial);
});
});
diff --git a/packages/expo-router/src/native-tabs/__tests__/options.e2e.test.ios.tsx b/packages/expo-router/src/native-tabs/__tests__/options.e2e.test.ios.tsx
index ce10612c809c7a..71feb320f95fad 100644
--- a/packages/expo-router/src/native-tabs/__tests__/options.e2e.test.ios.tsx
+++ b/packages/expo-router/src/native-tabs/__tests__/options.e2e.test.ios.tsx
@@ -1,10 +1,7 @@
import { screen, act, fireEvent } from '@testing-library/react-native';
import React from 'react';
import { Button, View } from 'react-native';
-import {
- BottomTabsScreen as _BottomTabsScreen,
- type BottomTabsScreenProps,
-} from 'react-native-screens';
+import { Tabs, type TabsScreenProps } from 'react-native-screens';
import { HrefPreview } from '../../link/preview/HrefPreview';
import { renderRouter, within } from '../../testing-library';
@@ -21,14 +18,20 @@ import type { NativeTabOptions } from '../types';
jest.mock('react-native-screens', () => {
const { View }: typeof import('react-native') = jest.requireActual('react-native');
+ const actualModule = jest.requireActual(
+ 'react-native-screens'
+ ) as typeof import('react-native-screens');
return {
- ...(jest.requireActual('react-native-screens') as typeof import('react-native-screens')),
- BottomTabs: jest.fn(({ children }) => {children}),
- BottomTabsScreen: jest.fn(({ children }) => {children}),
+ ...actualModule,
+ Tabs: {
+ ...actualModule.Tabs,
+ Host: jest.fn(({ children }) => {children}),
+ Screen: jest.fn(({ children }) => {children}),
+ },
};
});
-const BottomTabsScreen = _BottomTabsScreen as jest.MockedFunction;
+const TabsScreen = Tabs.Screen as jest.MockedFunction;
it('can pass props via unstable_nativeProps', () => {
const indexOptions = {
@@ -52,11 +55,11 @@ it('can pass props via unstable_nativeProps', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
...indexOptions,
});
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
...secondOptions,
});
});
@@ -74,15 +77,14 @@ it('can pass options via elements', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
icon: { ios: { type: 'sfSymbol', name: 'homepod.2.fill' } },
selectedIcon: undefined,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when no options are passed, default ones are used', () => {
- const { BottomTabsScreen } = require('react-native-screens');
renderRouter({
_layout: () => (
@@ -93,8 +95,8 @@ it('when no options are passed, default ones are used', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
hidden: false,
specialEffects: {},
tabKey: expect.stringMatching(/^index-[-\w]+/),
@@ -103,7 +105,7 @@ it('when no options are passed, default ones are used', () => {
icon: undefined,
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
describe('Icons', () => {
@@ -120,10 +122,10 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
icon: { ios: { type: 'sfSymbol', name: 'homepod.2.fill' } },
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when using Icon with sf selected prop, it is passed as selected icon sfSymbolName', () => {
@@ -139,10 +141,10 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
selectedIcon: { type: 'sfSymbol', name: 'homepod.2.fill' },
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when using Icon with sf object, values are passed correctly', () => {
@@ -158,11 +160,11 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
selectedIcon: { type: 'sfSymbol', name: 'star.bubble' },
icon: { ios: { type: 'sfSymbol', name: 'stairs' } },
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when using Icon drawable on iOS, no value is passed', () => {
@@ -178,9 +180,9 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].icon).toBeUndefined();
- expect(BottomTabsScreen.mock.calls[0][0].selectedIcon).toBeUndefined();
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].icon).toBeUndefined();
+ expect(TabsScreen.mock.calls[0][0].selectedIcon).toBeUndefined();
});
it('uses last Icon sf value when multiple are provided', () => {
@@ -197,11 +199,11 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
icon: { ios: { type: 'sfSymbol', name: 'homepod.2.fill' } },
selectedIcon: undefined,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('uses last Icon sf selected when multiple are provided', () => {
@@ -218,10 +220,10 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
selectedIcon: { type: 'sfSymbol', name: 'homepod.2.fill' },
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('uses last Icon sf for each type when multiple are provided', () => {
@@ -241,11 +243,11 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
selectedIcon: undefined,
icon: { ios: { type: 'sfSymbol', name: '0.circle.ar' } },
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('uses last Icon sf for each type when multiple are provided', () => {
@@ -265,11 +267,11 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
selectedIcon: { type: 'sfSymbol', name: '0.circle.ar' },
icon: undefined,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when selectedIconColor is provided, it is passed to screen', () => {
@@ -282,8 +284,8 @@ describe('Icons', () => {
index: () => ,
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -291,7 +293,7 @@ describe('Icons', () => {
},
},
},
- } as Partial);
+ } as Partial);
});
it('when selectedIconColor is provided in container and tab, the tab should use the tab color', () => {
@@ -308,8 +310,8 @@ describe('Icons', () => {
one: () => ,
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -317,8 +319,8 @@ describe('Icons', () => {
},
},
},
- } as Partial);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as Partial);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -326,7 +328,7 @@ describe('Icons', () => {
},
},
},
- } as Partial);
+ } as Partial);
});
it.each([
@@ -367,7 +369,7 @@ describe('Icons', () => {
MaterialIcon &
SrcIcon &
SFSymbolIcon & {
- expectedIcon: BottomTabsScreenProps['icon']['ios'];
+ expectedIcon: TabsScreenProps['icon']['ios'];
})[])(
'For , icon is $expectedIcon',
({ sf, src, drawable, material, expectedIcon }) => {
@@ -383,8 +385,8 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].icon?.ios).toEqual(expectedIcon);
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].icon?.ios).toEqual(expectedIcon);
}
);
});
@@ -403,10 +405,10 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
badgeValue: '5',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('passes badge value as string', () => {
@@ -422,10 +424,10 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
badgeValue: 'New',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('does not pass badge when Badge is not used', () => {
@@ -439,8 +441,8 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).not.toHaveProperty('badgeValue');
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).not.toHaveProperty('badgeValue');
});
it('uses last Badge value when multiple are provided', () => {
@@ -458,10 +460,10 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
badgeValue: '3',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when empty Badge is used, passes space to badgeValue', () => {
@@ -477,8 +479,8 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].badgeValue).toBe(' '); // Space is used to show empty badge
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].badgeValue).toBe(' '); // Space is used to show empty badge
});
it('when empty Badge is used with hidden, passes undefined to badgeValue', () => {
@@ -494,8 +496,8 @@ describe('Badge', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].badgeValue).toBeUndefined();
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].badgeValue).toBeUndefined();
});
});
@@ -513,10 +515,10 @@ describe('Label', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Custom Title',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when title is not set, uses the route name', () => {
@@ -533,9 +535,9 @@ describe('Label', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('one')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].title).toBe('index');
- expect(BottomTabsScreen.mock.calls[1][0].title).toBe('one');
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].title).toBe('index');
+ expect(TabsScreen.mock.calls[1][0].title).toBe('one');
});
it('uses last Label value when multiple are provided', () => {
@@ -553,10 +555,10 @@ describe('Label', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Last Title',
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('when empty Label is used, passes route name to title', () => {
@@ -572,8 +574,8 @@ describe('Label', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].title).toBe('index'); // Route name is used as title when Label is empty
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].title).toBe('index'); // Route name is used as title when Label is empty
});
it('when Label with hidden is used, passes empty string to title', () => {
@@ -589,8 +591,8 @@ describe('Label', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].title).toBe(''); // Route name is used as title when Label is empty
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].title).toBe(''); // Route name is used as title when Label is empty
});
it('when selectedLabelStyle is provided, it is passed to screen', () => {
@@ -603,8 +605,8 @@ describe('Label', () => {
index: () => ,
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -613,7 +615,7 @@ describe('Label', () => {
},
},
},
- } as Partial);
+ } as Partial);
});
it('when selectedLabelStyle is provided in container and tab, the tab should use the tab color', () => {
@@ -630,8 +632,8 @@ describe('Label', () => {
one: () => ,
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -640,8 +642,8 @@ describe('Label', () => {
},
},
},
- } as Partial);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as Partial);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
standardAppearance: {
stacked: {
selected: {
@@ -650,7 +652,7 @@ describe('Label', () => {
},
},
},
- } as Partial);
+ } as Partial);
});
});
@@ -669,14 +671,14 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
specialEffects: {
repeatedTabSelection: {
popToRoot: false,
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('When disablePopToTop is not set or false, popToRoot is true', () => {
@@ -696,23 +698,23 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Custom Title',
specialEffects: {
repeatedTabSelection: {
popToRoot: true,
},
},
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'One',
specialEffects: {
repeatedTabSelection: {
popToRoot: true,
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
});
@@ -730,14 +732,14 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
specialEffects: {
repeatedTabSelection: {
scrollToTop: false,
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('When disableScrollToTop is not set or false, scrollToTop is true', () => {
@@ -757,23 +759,23 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Custom Title',
specialEffects: {
repeatedTabSelection: {
scrollToTop: true,
},
},
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'One',
specialEffects: {
repeatedTabSelection: {
scrollToTop: true,
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it.each([true, false, undefined])(
@@ -789,10 +791,10 @@ describe('Tab options', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
overrideScrollViewContentInsetAdjustmentBehavior: !value,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
}
);
});
@@ -813,8 +815,8 @@ describe('Dynamic options', () => {
),
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
hidden: false,
specialEffects: {},
@@ -824,8 +826,8 @@ describe('Dynamic options', () => {
freezeContents: false,
icon: undefined,
selectedIcon: undefined,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Updated Title',
hidden: false,
specialEffects: {},
@@ -835,7 +837,7 @@ describe('Dynamic options', () => {
freezeContents: false,
icon: undefined,
selectedIcon: undefined,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('unstable_nativeProps override dynamic options configuration', () => {
@@ -856,8 +858,8 @@ describe('Dynamic options', () => {
),
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
hidden: false,
specialEffects: {},
@@ -866,8 +868,8 @@ describe('Dynamic options', () => {
icon: undefined,
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Initial Title',
hidden: false,
specialEffects: {},
@@ -882,7 +884,7 @@ describe('Dynamic options', () => {
},
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('can override component children from _layout with unstable_nativeProps', () => {
@@ -912,8 +914,8 @@ describe('Dynamic options', () => {
),
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
badgeValue: '3',
icon: {
@@ -926,8 +928,8 @@ describe('Dynamic options', () => {
specialEffects: {},
tabKey: expect.stringMatching(/^index-[-\w]+/),
isFocused: true,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Updated Title',
hidden: false,
specialEffects: {},
@@ -940,7 +942,7 @@ describe('Dynamic options', () => {
name: 'homepod.2.fill',
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('can override component children from _layout with dynamic children', () => {
@@ -965,8 +967,8 @@ describe('Dynamic options', () => {
),
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
badgeValue: '3',
icon: {
@@ -979,8 +981,8 @@ describe('Dynamic options', () => {
specialEffects: {},
tabKey: expect.stringMatching(/^index-[-\w]+/),
isFocused: true,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Updated Title',
hidden: false,
specialEffects: {},
@@ -993,7 +995,7 @@ describe('Dynamic options', () => {
name: 'homepod.2.fill',
},
},
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
it('can dynamically update options with state update', () => {
@@ -1019,15 +1021,15 @@ describe('Dynamic options', () => {
},
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].title).toBe('Initial Title');
- expect(BottomTabsScreen.mock.calls[1][0].title).toBe('Updated Title 0');
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].title).toBe('Initial Title');
+ expect(TabsScreen.mock.calls[1][0].title).toBe('Updated Title 0');
act(() => fireEvent.press(screen.getByTestId('update-button')));
- expect(BottomTabsScreen).toHaveBeenCalledTimes(3);
- expect(BottomTabsScreen.mock.calls[2][0].title).toBe('Updated Title 1');
+ expect(TabsScreen).toHaveBeenCalledTimes(3);
+ expect(TabsScreen.mock.calls[2][0].title).toBe('Updated Title 1');
act(() => fireEvent.press(screen.getByTestId('update-button')));
- expect(BottomTabsScreen).toHaveBeenCalledTimes(4);
- expect(BottomTabsScreen.mock.calls[3][0].title).toBe('Updated Title 2');
+ expect(TabsScreen).toHaveBeenCalledTimes(4);
+ expect(TabsScreen.mock.calls[3][0].title).toBe('Updated Title 2');
});
it('can be used in preview', () => {
@@ -1061,8 +1063,8 @@ describe('Dynamic options', () => {
// Tab + preview
expect(screen.getAllByTestId('second')).toHaveLength(2);
expect(within(screen.getByTestId('index')).getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0]).toMatchObject({
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0]).toMatchObject({
title: 'Initial Title',
hidden: false,
specialEffects: {},
@@ -1071,8 +1073,8 @@ describe('Dynamic options', () => {
icon: undefined,
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
- expect(BottomTabsScreen.mock.calls[1][0]).toMatchObject({
+ } as TabsScreenProps);
+ expect(TabsScreen.mock.calls[1][0]).toMatchObject({
title: 'Second',
hidden: false,
specialEffects: {},
@@ -1081,7 +1083,7 @@ describe('Dynamic options', () => {
icon: undefined,
selectedIcon: undefined,
freezeContents: false,
- } as BottomTabsScreenProps);
+ } as TabsScreenProps);
});
});
diff --git a/packages/expo-router/src/native-tabs/__tests__/options.test.android.tsx b/packages/expo-router/src/native-tabs/__tests__/options.test.android.tsx
index 336fa9db80f4ed..f81d80e54e4d06 100644
--- a/packages/expo-router/src/native-tabs/__tests__/options.test.android.tsx
+++ b/packages/expo-router/src/native-tabs/__tests__/options.test.android.tsx
@@ -1,7 +1,7 @@
import { screen } from '@testing-library/react-native';
import React from 'react';
import { View } from 'react-native';
-import { BottomTabsScreen as _BottomTabsScreen } from 'react-native-screens';
+import { Tabs } from 'react-native-screens';
import { renderRouter } from '../../testing-library';
import { appendIconOptions } from '../NativeTabTrigger';
@@ -11,14 +11,20 @@ import type { NativeTabOptions } from '../types';
jest.mock('react-native-screens', () => {
const { View }: typeof import('react-native') = jest.requireActual('react-native');
+ const actualModule = jest.requireActual(
+ 'react-native-screens'
+ ) as typeof import('react-native-screens');
return {
- ...(jest.requireActual('react-native-screens') as typeof import('react-native-screens')),
- BottomTabs: jest.fn(({ children }) => {children}),
- BottomTabsScreen: jest.fn(({ children }) => {children}),
+ ...actualModule,
+ Tabs: {
+ ...actualModule.Tabs,
+ Host: jest.fn(({ children }) => {children}),
+ Screen: jest.fn(({ children }) => {children}),
+ },
};
});
-const BottomTabsScreen = _BottomTabsScreen as jest.MockedFunction;
+const TabsScreen = Tabs.Screen as jest.MockedFunction;
describe('Icons', () => {
it('passes iconResourceName when using Icon drawable on Android', () => {
@@ -34,10 +40,10 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].icon.android.type).toBe('drawableResource');
- if (BottomTabsScreen.mock.calls[0][0].icon.android.type === 'drawableResource') {
- expect(BottomTabsScreen.mock.calls[0][0].icon.android.name).toBe('stairs');
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].icon.android.type).toBe('drawableResource');
+ if (TabsScreen.mock.calls[0][0].icon.android.type === 'drawableResource') {
+ expect(TabsScreen.mock.calls[0][0].icon.android.name).toBe('stairs');
}
});
@@ -56,10 +62,10 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].icon.android.type).toBe('drawableResource');
- if (BottomTabsScreen.mock.calls[0][0].icon.android.type === 'drawableResource') {
- expect(BottomTabsScreen.mock.calls[0][0].icon.android.name).toBe('last');
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].icon.android.type).toBe('drawableResource');
+ if (TabsScreen.mock.calls[0][0].icon.android.type === 'drawableResource') {
+ expect(TabsScreen.mock.calls[0][0].icon.android.name).toBe('last');
}
});
@@ -74,8 +80,8 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].icon).toBeUndefined();
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].icon).toBeUndefined();
});
// Currently not needed. Screens does not forbid this, as Icon does not work on Android yet.
@@ -108,11 +114,11 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].selectedIcon).toBeUndefined();
- expect(BottomTabsScreen.mock.calls[0][0].icon.android.type).toBe('drawableResource');
- if (BottomTabsScreen.mock.calls[0][0].icon.android.type === 'drawableResource') {
- expect(BottomTabsScreen.mock.calls[0][0].icon.android.name).toBe('stairs');
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].selectedIcon).toBeUndefined();
+ expect(TabsScreen.mock.calls[0][0].icon.android.type).toBe('drawableResource');
+ if (TabsScreen.mock.calls[0][0].icon.android.type === 'drawableResource') {
+ expect(TabsScreen.mock.calls[0][0].icon.android.name).toBe('stairs');
}
});
@@ -132,11 +138,11 @@ describe('Icons', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].selectedIcon).toBeUndefined();
- expect(BottomTabsScreen.mock.calls[0][0].icon.android.type).toBe('drawableResource');
- if (BottomTabsScreen.mock.calls[0][0].icon.android.type === 'drawableResource') {
- expect(BottomTabsScreen.mock.calls[0][0].icon.android.name).toBe('stairs');
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].selectedIcon).toBeUndefined();
+ expect(TabsScreen.mock.calls[0][0].icon.android.type).toBe('drawableResource');
+ if (TabsScreen.mock.calls[0][0].icon.android.type === 'drawableResource') {
+ expect(TabsScreen.mock.calls[0][0].icon.android.name).toBe('stairs');
}
});
});
diff --git a/packages/expo-router/src/native-tabs/__tests__/options.test.ios.tsx b/packages/expo-router/src/native-tabs/__tests__/options.test.ios.tsx
index 61674fda2fe695..225888cafda514 100644
--- a/packages/expo-router/src/native-tabs/__tests__/options.test.ios.tsx
+++ b/packages/expo-router/src/native-tabs/__tests__/options.test.ios.tsx
@@ -19,10 +19,16 @@ import type { NativeTabOptions } from '../types';
jest.mock('react-native-screens', () => {
const { View }: typeof import('react-native') = jest.requireActual('react-native');
+ const actualModule = jest.requireActual(
+ 'react-native-screens'
+ ) as typeof import('react-native-screens');
return {
- ...(jest.requireActual('react-native-screens') as typeof import('react-native-screens')),
- BottomTabs: jest.fn(({ children }) => {children}),
- BottomTabsScreen: jest.fn(({ children }) => {children}),
+ ...actualModule,
+ Tabs: {
+ ...actualModule.Tabs,
+ Host: jest.fn(({ children }) => {children}),
+ Screen: jest.fn(({ children }) => {children}),
+ },
};
});
diff --git a/packages/expo-router/src/native-tabs/__tests__/render.test.ios.tsx b/packages/expo-router/src/native-tabs/__tests__/render.test.ios.tsx
index 01667cd201be30..78474cac6126f4 100644
--- a/packages/expo-router/src/native-tabs/__tests__/render.test.ios.tsx
+++ b/packages/expo-router/src/native-tabs/__tests__/render.test.ios.tsx
@@ -2,10 +2,7 @@ import { usePreventRemove } from '@react-navigation/core';
import { screen } from '@testing-library/react-native';
import React, { isValidElement } from 'react';
import { Button, View } from 'react-native';
-import {
- BottomTabsScreen as _BottomTabsScreen,
- BottomTabs as _BottomTabs,
-} from 'react-native-screens';
+import { Tabs } from 'react-native-screens';
import { usePathname } from '../../hooks';
import { router } from '../../imperative-api';
@@ -23,10 +20,16 @@ import {
jest.mock('react-native-screens', () => {
const { View }: typeof import('react-native') = jest.requireActual('react-native');
+ const actualModule = jest.requireActual(
+ 'react-native-screens'
+ ) as typeof import('react-native-screens');
return {
- ...(jest.requireActual('react-native-screens') as typeof import('react-native-screens')),
- BottomTabs: jest.fn(({ children }) => {children}),
- BottomTabsScreen: jest.fn(({ children }) => {children}),
+ ...actualModule,
+ Tabs: {
+ ...actualModule.Tabs,
+ Host: jest.fn(({ children }) => {children}),
+ Screen: jest.fn(({ children }) => {children}),
+ },
};
});
@@ -38,8 +41,8 @@ jest.mock('../NativeTabsView', () => {
};
});
-const BottomTabsScreen = _BottomTabsScreen as jest.MockedFunction;
-const BottomTabs = _BottomTabs as jest.MockedFunction;
+const TabsScreen = Tabs.Screen as jest.MockedFunction;
+const TabsHost = Tabs.Host as jest.MockedFunction;
const warn = jest.fn();
const error = jest.fn();
@@ -70,7 +73,7 @@ it('renders tabs correctly', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
});
describe('Tabs visibility', () => {
@@ -90,7 +93,7 @@ describe('Tabs visibility', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
expect(screen.queryByTestId('third')).toBeNull();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
});
it('does not render hidden tabs', () => {
@@ -115,7 +118,7 @@ describe('Tabs visibility', () => {
expect(screen.queryByTestId('third')).toBeNull();
expect(screen.queryByTestId('fourth')).toBeNull();
expect(screen.getByTestId('fifth')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(3);
+ expect(TabsScreen).toHaveBeenCalledTimes(3);
});
it('does not render tabs, when route does not exist', () => {
@@ -131,7 +134,7 @@ describe('Tabs visibility', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.queryByTestId('second')).toBeNull();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
expect(warn).toHaveBeenCalledTimes(1);
expect(warn).toHaveBeenCalledWith(
'[Layout children]: Too many screens defined. Route "second" is extraneous.'
@@ -154,11 +157,11 @@ describe('First focused tab', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[0][0].tabKey).toMatch(/^index-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[1][0].isFocused).toBe(false);
- expect(BottomTabsScreen.mock.calls[1][0].tabKey).toMatch(/^second-[-\w]+/);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[0][0].tabKey).toMatch(/^index-[-\w]+/);
+ expect(TabsScreen.mock.calls[1][0].isFocused).toBe(false);
+ expect(TabsScreen.mock.calls[1][0].tabKey).toMatch(/^second-[-\w]+/);
});
it('index tab is focused when it is second tab', () => {
@@ -175,11 +178,11 @@ describe('First focused tab', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].isFocused).toBe(false);
- expect(BottomTabsScreen.mock.calls[0][0].tabKey).toMatch(/^second-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[1][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[1][0].tabKey).toMatch(/^index-[-\w]+/);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].isFocused).toBe(false);
+ expect(TabsScreen.mock.calls[0][0].tabKey).toMatch(/^second-[-\w]+/);
+ expect(TabsScreen.mock.calls[1][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[1][0].tabKey).toMatch(/^index-[-\w]+/);
});
describe('First tab is used, when index is hidden', () => {
@@ -235,7 +238,7 @@ describe('First focused tab', () => {
expect(screen.getByTestId('expo-router-unmatched')).toBeVisible();
expect(screen.queryByTestId('first')).toBeNull();
expect(screen.queryByTestId('second')).toBeNull();
- expect(BottomTabsScreen).not.toHaveBeenCalled();
+ expect(TabsScreen).not.toHaveBeenCalled();
});
it('Correct tab is shown, when index is hidden and redirect is set in layout', () => {
@@ -260,11 +263,11 @@ describe('First focused tab', () => {
expect(screen.getByTestId('first')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].isFocused).toBe(false);
- expect(BottomTabsScreen.mock.calls[0][0].tabKey).toMatch(/^first-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[1][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[1][0].tabKey).toMatch(/^second-[-\w]+/);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].isFocused).toBe(false);
+ expect(TabsScreen.mock.calls[0][0].tabKey).toMatch(/^first-[-\w]+/);
+ expect(TabsScreen.mock.calls[1][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[1][0].tabKey).toMatch(/^second-[-\w]+/);
});
it('Correct tab is shown, when index does not exist, redirect is set in layout and +not-found is specified', () => {
@@ -289,11 +292,11 @@ describe('First focused tab', () => {
expect(screen.getByTestId('first')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].isFocused).toBe(false);
- expect(BottomTabsScreen.mock.calls[0][0].tabKey).toMatch(/^first-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[1][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[1][0].tabKey).toMatch(/^second-[-\w]+/);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].isFocused).toBe(false);
+ expect(TabsScreen.mock.calls[0][0].tabKey).toMatch(/^first-[-\w]+/);
+ expect(TabsScreen.mock.calls[1][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[1][0].tabKey).toMatch(/^second-[-\w]+/);
});
it('404 is shown, when index does not exist, redirect is set in layout and no +not-found is specified', () => {
@@ -318,7 +321,7 @@ describe('First focused tab', () => {
expect(screen.getByTestId('expo-router-unmatched')).toBeVisible();
expect(screen.queryByTestId('first')).toBeNull();
expect(screen.queryByTestId('second')).toBeNull();
- expect(BottomTabsScreen).not.toHaveBeenCalled();
+ expect(TabsScreen).not.toHaveBeenCalled();
});
it('Can remove the last tab, when it is focused', async () => {
@@ -348,37 +351,37 @@ describe('First focused tab', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(2);
- expect(BottomTabsScreen.mock.calls[0][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[0][0].tabKey).toMatch(/^index-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[1][0].isFocused).toBe(false);
- expect(BottomTabsScreen.mock.calls[1][0].tabKey).toMatch(/^second-[-\w]+/);
+ expect(TabsScreen).toHaveBeenCalledTimes(2);
+ expect(TabsScreen.mock.calls[0][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[0][0].tabKey).toMatch(/^index-[-\w]+/);
+ expect(TabsScreen.mock.calls[1][0].isFocused).toBe(false);
+ expect(TabsScreen.mock.calls[1][0].tabKey).toMatch(/^second-[-\w]+/);
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => router.navigate('/second'));
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(4);
- expect(BottomTabsScreen.mock.calls[2][0].isFocused).toBe(false);
- expect(BottomTabsScreen.mock.calls[2][0].tabKey).toMatch(/^index-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[3][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[3][0].tabKey).toMatch(/^second-[-\w]+/);
+ expect(TabsScreen).toHaveBeenCalledTimes(4);
+ expect(TabsScreen.mock.calls[2][0].isFocused).toBe(false);
+ expect(TabsScreen.mock.calls[2][0].tabKey).toMatch(/^index-[-\w]+/);
+ expect(TabsScreen.mock.calls[3][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[3][0].tabKey).toMatch(/^second-[-\w]+/);
- BottomTabsScreen.mockClear();
+ TabsScreen.mockClear();
act(() => {
fireEvent.press(screen.getByTestId('remove'));
});
expect(screen.queryByTestId('second')).toBeNull();
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(3);
- expect(BottomTabsScreen.mock.calls[0][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[0][0].tabKey).toMatch(/^index-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[1][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[1][0].tabKey).toMatch(/^index-[-\w]+/);
- expect(BottomTabsScreen.mock.calls[2][0].isFocused).toBe(true);
- expect(BottomTabsScreen.mock.calls[2][0].tabKey).toMatch(/^index-[-\w]+/);
+ expect(TabsScreen).toHaveBeenCalledTimes(3);
+ expect(TabsScreen.mock.calls[0][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[0][0].tabKey).toMatch(/^index-[-\w]+/);
+ expect(TabsScreen.mock.calls[1][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[1][0].tabKey).toMatch(/^index-[-\w]+/);
+ expect(TabsScreen.mock.calls[2][0].isFocused).toBe(true);
+ expect(TabsScreen.mock.calls[2][0].tabKey).toMatch(/^index-[-\w]+/);
});
});
@@ -420,9 +423,9 @@ describe('Native props validation', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].standardAppearance.tabBarBlurEffect).toBe(blurEffect);
- expect(BottomTabsScreen.mock.calls[0][0].scrollEdgeAppearance.tabBarBlurEffect).toBe('none');
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].standardAppearance.tabBarBlurEffect).toBe(blurEffect);
+ expect(TabsScreen.mock.calls[0][0].scrollEdgeAppearance.tabBarBlurEffect).toBe('none');
});
it.each(['test', 'wrongValue', ...SUPPORTED_BLUR_EFFECTS.map((x) => x.toUpperCase())])(
'warns when unsupported %s blur effect is used',
@@ -440,9 +443,9 @@ describe('Native props validation', () => {
expect(warn).toHaveBeenCalledWith(
`Unsupported blurEffect: ${blurEffect}. Supported values are: ${SUPPORTED_BLUR_EFFECTS.map((effect) => `"${effect}"`).join(', ')}`
);
- expect(BottomTabsScreen).toHaveBeenCalledTimes(1);
- expect(BottomTabsScreen.mock.calls[0][0].standardAppearance.tabBarBlurEffect).toBe(undefined);
- expect(BottomTabsScreen.mock.calls[0][0].scrollEdgeAppearance.tabBarBlurEffect).toBe('none');
+ expect(TabsScreen).toHaveBeenCalledTimes(1);
+ expect(TabsScreen.mock.calls[0][0].standardAppearance.tabBarBlurEffect).toBe(undefined);
+ expect(TabsScreen.mock.calls[0][0].scrollEdgeAppearance.tabBarBlurEffect).toBe('none');
}
);
it.each(SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES)(
@@ -458,8 +461,8 @@ describe('Native props validation', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0].tabBarItemLabelVisibilityMode).toBe(labelVisibilityMode);
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0].tabBarItemLabelVisibilityMode).toBe(labelVisibilityMode);
}
);
it.each([
@@ -480,8 +483,8 @@ describe('Native props validation', () => {
expect(warn).toHaveBeenCalledWith(
`Unsupported labelVisibilityMode: ${labelVisibilityMode}. Supported values are: ${SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES.map((effect) => `"${effect}"`).join(', ')}`
);
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0].tabBarItemLabelVisibilityMode).toBe(undefined);
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0].tabBarItemLabelVisibilityMode).toBe(undefined);
});
it.each(SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS)(
'supports %s minimize behavior',
@@ -496,8 +499,8 @@ describe('Native props validation', () => {
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0].tabBarMinimizeBehavior).toBe(minimizeBehavior);
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0].tabBarMinimizeBehavior).toBe(minimizeBehavior);
}
);
it.each([
@@ -518,8 +521,8 @@ describe('Native props validation', () => {
expect(warn).toHaveBeenCalledWith(
`Unsupported minimizeBehavior: ${minimizeBehavior}. Supported values are: ${SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS.map((effect) => `"${effect}"`).join(', ')}`
);
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0].tabBarMinimizeBehavior).toBe(undefined);
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0].tabBarMinimizeBehavior).toBe(undefined);
});
});
@@ -560,10 +563,10 @@ describe('Misc', () => {
index: () => ,
});
expect(screen.getByTestId('index')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0].bottomAccessory).toBeDefined();
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0].bottomAccessory).toBeDefined();
- const bottomAccessoryFn = BottomTabs.mock.calls[0][0].bottomAccessory!;
+ const bottomAccessoryFn = TabsHost.mock.calls[0][0].bottomAccessory!;
const regularRender = bottomAccessoryFn('regular');
const inlineRender = bottomAccessoryFn('inline');
@@ -582,7 +585,7 @@ describe('Misc', () => {
{ hidden: true, expected: true },
{ hidden: false, expected: false },
{ hidden: undefined, expected: undefined },
- ])('passes hidden=$hidden prop to BottomTabs', ({ hidden, expected }) => {
+ ])('passes hidden=$hidden prop to TabsHost', ({ hidden, expected }) => {
renderRouter({
_layout: () => (
@@ -596,7 +599,7 @@ describe('Misc', () => {
expect(screen.getByTestId('index')).toBeVisible();
expect(screen.getByTestId('second')).toBeVisible();
- expect(BottomTabs).toHaveBeenCalledTimes(1);
- expect(BottomTabs.mock.calls[0][0].tabBarHidden).toBe(expected);
+ expect(TabsHost).toHaveBeenCalledTimes(1);
+ expect(TabsHost.mock.calls[0][0].tabBarHidden).toBe(expected);
});
});
diff --git a/packages/expo-router/src/native-tabs/appearance.ts b/packages/expo-router/src/native-tabs/appearance.ts
index 4175c06ebca687..037087365fa553 100644
--- a/packages/expo-router/src/native-tabs/appearance.ts
+++ b/packages/expo-router/src/native-tabs/appearance.ts
@@ -1,8 +1,8 @@
import type { ColorValue } from 'react-native';
import type {
- BottomTabsScreenAppearance,
- BottomTabsScreenItemAppearance,
- BottomTabsScreenItemStateAppearance,
+ TabsScreenAppearance,
+ TabsScreenItemAppearance,
+ TabsScreenItemStateAppearance,
} from 'react-native-screens';
import {
@@ -17,7 +17,7 @@ const supportedBlurEffectsSet = new Set(SUPPORTED_BLUR_EFFECTS);
export function createStandardAppearanceFromOptions(
options: NativeTabOptions
-): BottomTabsScreenAppearance {
+): TabsScreenAppearance {
let blurEffect = options.blurEffect;
if (blurEffect && !supportedBlurEffectsSet.has(blurEffect)) {
console.warn(
@@ -53,7 +53,7 @@ export function createStandardAppearanceFromOptions(
export function createScrollEdgeAppearanceFromOptions(
options: NativeTabOptions
-): BottomTabsScreenAppearance {
+): TabsScreenAppearance {
let blurEffect = options.disableTransparentOnScrollEdge ? options.blurEffect : 'none';
if (blurEffect && !supportedBlurEffectsSet.has(blurEffect)) {
console.warn(
@@ -101,12 +101,12 @@ export interface AppearanceStyle extends NativeTabsLabelStyle {
export function appendSelectedStyleToAppearance(
selectedStyle: AppearanceStyle,
- appearance: BottomTabsScreenAppearance
-): BottomTabsScreenAppearance {
+ appearance: TabsScreenAppearance
+): TabsScreenAppearance {
return appendStyleToAppearance(selectedStyle, appearance, ['selected', 'focused']);
}
-const EMPTY_APPEARANCE_ITEM: BottomTabsScreenItemAppearance = {
+const EMPTY_APPEARANCE_ITEM: TabsScreenItemAppearance = {
normal: {},
selected: {},
focused: {},
@@ -115,9 +115,9 @@ const EMPTY_APPEARANCE_ITEM: BottomTabsScreenItemAppearance = {
export function appendStyleToAppearance(
style: AppearanceStyle,
- appearance: BottomTabsScreenAppearance,
+ appearance: TabsScreenAppearance,
states: ('selected' | 'focused' | 'disabled' | 'normal')[]
-): BottomTabsScreenAppearance {
+): TabsScreenAppearance {
const baseItemAppearance =
appearance.stacked || appearance.inline || appearance.compactInline || {};
@@ -131,7 +131,7 @@ export function appendStyleToAppearance(
},
}));
- const itemAppearance: BottomTabsScreenItemAppearance = {
+ const itemAppearance: TabsScreenItemAppearance = {
...EMPTY_APPEARANCE_ITEM,
...baseItemAppearance,
...Object.fromEntries(newAppearances.map(({ key, appearance }) => [key, appearance])),
@@ -149,14 +149,12 @@ export function appendStyleToAppearance(
};
}
-export function convertStyleToAppearance(
- style: AppearanceStyle | undefined
-): BottomTabsScreenAppearance {
+export function convertStyleToAppearance(style: AppearanceStyle | undefined): TabsScreenAppearance {
if (!style) {
return {};
}
const stateAppearance = convertStyleToItemStateAppearance(style);
- const itemAppearance: BottomTabsScreenItemAppearance = {
+ const itemAppearance: TabsScreenItemAppearance = {
normal: stateAppearance,
selected: stateAppearance,
focused: stateAppearance,
@@ -174,11 +172,11 @@ export function convertStyleToAppearance(
export function convertStyleToItemStateAppearance(
style: AppearanceStyle | undefined
-): BottomTabsScreenItemStateAppearance {
+): TabsScreenItemStateAppearance {
if (!style) {
return {};
}
- const stateAppearance: BottomTabsScreenItemStateAppearance = {
+ const stateAppearance: TabsScreenItemStateAppearance = {
tabBarItemBadgeBackgroundColor: style.badgeBackgroundColor,
tabBarItemTitlePositionAdjustment: style.titlePositionAdjustment,
tabBarItemIconColor: style.iconColor,
@@ -189,7 +187,7 @@ export function convertStyleToItemStateAppearance(
tabBarItemTitleFontColor: style.color,
};
- (Object.keys(stateAppearance) as (keyof BottomTabsScreenItemStateAppearance)[]).forEach((key) => {
+ (Object.keys(stateAppearance) as (keyof TabsScreenItemStateAppearance)[]).forEach((key) => {
if (stateAppearance[key] === undefined) {
delete stateAppearance[key];
}
diff --git a/packages/expo-router/src/native-tabs/types.ts b/packages/expo-router/src/native-tabs/types.ts
index 3f0cc5f4a76398..35d99c4d72cbe5 100644
--- a/packages/expo-router/src/native-tabs/types.ts
+++ b/packages/expo-router/src/native-tabs/types.ts
@@ -7,10 +7,10 @@ import type {
TextStyle,
ViewStyle,
} from 'react-native';
-import type { BottomTabsScreenProps } from 'react-native-screens';
+import type { TabsScreenProps } from 'react-native-screens';
import type { SFSymbol } from 'sf-symbols-typescript';
-export type NativeScreenProps = Partial>;
+export type NativeScreenProps = Partial>;
export interface NativeTabOptions extends DefaultRouterOptions {
icon?: SymbolOrImageSource;
@@ -39,7 +39,7 @@ export interface NativeTabOptions extends DefaultRouterOptions {
};
indicatorColor?: ColorValue;
hidden?: boolean;
- specialEffects?: BottomTabsScreenProps['specialEffects'];
+ specialEffects?: TabsScreenProps['specialEffects'];
nativeProps?: NativeScreenProps;
disableAutomaticContentInsets?: boolean;
contentStyle?: Pick<
diff --git a/packages/expo-router/src/native-tabs/utils/bottomAccessory.tsx b/packages/expo-router/src/native-tabs/utils/bottomAccessory.tsx
index 0fbe25e2d9ca16..51a650612f35fe 100644
--- a/packages/expo-router/src/native-tabs/utils/bottomAccessory.tsx
+++ b/packages/expo-router/src/native-tabs/utils/bottomAccessory.tsx
@@ -1,5 +1,5 @@
import { useMemo, type ReactElement } from 'react';
-import type { BottomAccessoryFn } from 'react-native-screens';
+import type { TabAccessoryComponentFactory } from 'react-native-screens';
import type { NativeTabsBottomAccessoryProps } from '../common/elements';
import { BottomAccessoryPlacementContext } from '../hooks';
@@ -12,8 +12,8 @@ export function useBottomAccessoryFunctionFromBottomAccessories(
bottomAccessory:
| ReactElement>
| undefined
-): BottomAccessoryFn | undefined {
- return useMemo(
+): TabAccessoryComponentFactory | undefined {
+ return useMemo(
() =>
bottomAccessory
? (environment) => (
diff --git a/packages/expo-router/src/native-tabs/utils/icon.ts b/packages/expo-router/src/native-tabs/utils/icon.ts
index 6c869517947f03..2892c97adcf506 100644
--- a/packages/expo-router/src/native-tabs/utils/icon.ts
+++ b/packages/expo-router/src/native-tabs/utils/icon.ts
@@ -1,10 +1,6 @@
import { useEffect, useMemo, useState } from 'react';
import type { ColorValue, ImageSourcePropType } from 'react-native';
-import type {
- BottomTabsScreenProps,
- PlatformIconAndroid,
- PlatformIconIOS,
-} from 'react-native-screens';
+import type { TabsScreenProps, PlatformIconAndroid, PlatformIconIOS } from 'react-native-screens';
import type { SFSymbol } from 'sf-symbols-typescript';
import { isChildOfType } from '../../utils/children';
@@ -66,7 +62,7 @@ function isAwaitedIcon(icon: NativeTabOptions['icon']): icon is AwaitedIcon {
export function convertOptionsIconToRNScreensPropsIcon(
icon: AwaitedIcon | undefined
-): BottomTabsScreenProps['icon'] {
+): TabsScreenProps['icon'] {
if (!icon) {
return undefined;
}
diff --git a/packages/expo-router/src/split-view/elements.tsx b/packages/expo-router/src/split-view/elements.tsx
index 21a4d117e461bf..acd398f473907d 100644
--- a/packages/expo-router/src/split-view/elements.tsx
+++ b/packages/expo-router/src/split-view/elements.tsx
@@ -1,5 +1,5 @@
import { SafeAreaProvider } from 'react-native-safe-area-context';
-import { SplitViewScreen } from 'react-native-screens/experimental';
+import { Split } from 'react-native-screens/experimental';
export interface SplitViewColumnProps {
children?: React.ReactNode;
@@ -7,9 +7,9 @@ export interface SplitViewColumnProps {
export function SplitViewColumn(props: SplitViewColumnProps) {
return (
-
+ {props.children}
-
+
);
}
@@ -17,5 +17,5 @@ export function SplitViewColumn(props: SplitViewColumnProps) {
* @platform iOS 26+
*/
export function SplitViewInspector(props: SplitViewColumnProps) {
- return {props.children};
+ return {props.children};
}
diff --git a/packages/expo-router/src/split-view/index.ts b/packages/expo-router/src/split-view/index.ts
index ba77a86238e5f4..ff4ceccc79d076 100644
--- a/packages/expo-router/src/split-view/index.ts
+++ b/packages/expo-router/src/split-view/index.ts
@@ -1,3 +1,3 @@
export { SplitView, SplitViewProps } from './split-view';
export * from './elements';
-export { type SplitViewHostProps } from 'react-native-screens/experimental';
+export { type SplitHostProps } from 'react-native-screens/experimental';
diff --git a/packages/expo-router/src/split-view/split-view.tsx b/packages/expo-router/src/split-view/split-view.tsx
index 17f24a6d93ace9..33d1897e547619 100644
--- a/packages/expo-router/src/split-view/split-view.tsx
+++ b/packages/expo-router/src/split-view/split-view.tsx
@@ -1,9 +1,5 @@
import React, { createContext, isValidElement, use, type ReactNode } from 'react';
-import {
- SplitViewHost,
- SplitViewScreen,
- type SplitViewHostProps,
-} from 'react-native-screens/experimental';
+import { Split, type SplitHostProps } from 'react-native-screens/experimental';
import { SplitViewColumn, SplitViewInspector } from './elements';
import { IsWithinLayoutContext } from '../layouts/IsWithinLayoutContext';
@@ -12,9 +8,9 @@ import { Slot } from '../views/Navigator';
const IsWithinSplitViewContext = createContext(false);
/**
- * For full list of supported props, see [`SplitViewHostProps`](https://github.com/software-mansion/react-native-screens/blob/main/src/components/gamma/split-view/SplitViewHost.types.ts#L124)
+ * For full list of supported props, see [`SplitHostProps`](http://github.com/software-mansion/react-native-screens/blob/main/src/components/gamma/split/SplitHost.types.ts#L117)
*/
-export interface SplitViewProps extends Omit {
+export interface SplitViewProps extends Omit {
children?: ReactNode;
}
@@ -68,13 +64,13 @@ function SplitViewNavigator({ children, ...splitViewHostProps }: SplitViewProps)
// The key is needed, because number of columns cannot be changed dynamically
return (
-
+
{columnChildren}
-
+
-
+
{inspectorChildren}
-
+
);
}
diff --git a/packages/expo-server/CHANGELOG.md b/packages/expo-server/CHANGELOG.md
index cf9cd8f24b5779..86b5eaa45bc7a5 100644
--- a/packages/expo-server/CHANGELOG.md
+++ b/packages/expo-server/CHANGELOG.md
@@ -9,6 +9,7 @@
- Improve SSR support ([#41477](https://github.com/expo/expo/pull/41477) by [@hassankhan](https://github.com/hassankhan))
- Add support for server data loaders in server export mode ([#41934](https://github.com/expo/expo/pull/41934) by [@hassankhan](https://github.com/hassankhan))
- Respect `web.output` configuration in dev server for loaders ([#42147](https://github.com/expo/expo/pull/42147) by [@hassankhan](https://github.com/hassankhan))
+- Allow returning `Response` objects from loader functions ([#42051](https://github.com/expo/expo/pull/42051) by [@hassankhan](https://github.com/hassankhan))
### 🐛 Bug fixes
diff --git a/packages/expo-server/build/cjs/vendor/abstract.d.ts b/packages/expo-server/build/cjs/vendor/abstract.d.ts
index dc9446e2252c3c..be2cdc116486c6 100644
--- a/packages/expo-server/build/cjs/vendor/abstract.d.ts
+++ b/packages/expo-server/build/cjs/vendor/abstract.d.ts
@@ -36,9 +36,7 @@ export interface RequestHandlerInput {
getRoutesManifest(): Promise;
getApiRoute(route: Route): Promise;
getMiddleware(route: MiddlewareInfo): Promise;
- getLoaderData(request: Request, route: Route): Promise<{
- data: unknown;
- } | undefined>;
+ getLoaderData(request: Request, route: Route): Promise;
}
export declare function createRequestHandler({ getRoutesManifest, getHtml, getApiRoute, getMiddleware, getLoaderData, beforeErrorResponse, beforeResponse, beforeHTMLResponse, beforeAPIResponse, }: RequestHandlerParams & RequestHandlerInput): (request: Request) => Promise;
export {};
diff --git a/packages/expo-server/build/cjs/vendor/abstract.js b/packages/expo-server/build/cjs/vendor/abstract.js
index 2b9f51f335d8a0..7d557e007082ce 100644
--- a/packages/expo-server/build/cjs/vendor/abstract.js
+++ b/packages/expo-server/build/cjs/vendor/abstract.js
@@ -98,13 +98,7 @@ function createRequestHandler({ getRoutesManifest, getHtml, getApiRoute, getMidd
// NOTE(@hassankhan): Relocate the request rewriting logic from here
url.pathname = matchedPath;
const loaderRequest = new Request(url, request);
- const loaderResult = await getLoaderData(loaderRequest, route);
- return createResponse('api', route, JSON.stringify(loaderResult?.data), {
- status: 200,
- headers: new Headers({
- 'Content-Type': 'application/json',
- }),
- });
+ return createResponseFrom('api', route, await getLoaderData(loaderRequest, route));
}
const html = await getHtml(request, route);
return respondHTML(html, route);
diff --git a/packages/expo-server/build/cjs/vendor/abstract.js.map b/packages/expo-server/build/cjs/vendor/abstract.js.map
index 1cd1c4bcfcfd5f..4c5cf82c5b85eb 100644
--- a/packages/expo-server/build/cjs/vendor/abstract.js.map
+++ b/packages/expo-server/build/cjs/vendor/abstract.js.map
@@ -1 +1 @@
-{"version":3,"file":"abstract.js","sourceRoot":"","sources":["../../../src/vendor/abstract.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"abstract.js","sourceRoot":"","sources":["../../../src/vendor/abstract.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-server/build/cjs/vendor/environment/common.d.ts b/packages/expo-server/build/cjs/vendor/environment/common.d.ts
index 5b777a66e8e601..5435517b26e6d6 100644
--- a/packages/expo-server/build/cjs/vendor/environment/common.d.ts
+++ b/packages/expo-server/build/cjs/vendor/environment/common.d.ts
@@ -9,8 +9,6 @@ export declare function createEnvironment(input: EnvironmentInput): {
getHtml(request: Request, route: Route): Promise;
getApiRoute(route: Route): Promise;
getMiddleware(middleware: MiddlewareInfo): Promise;
- getLoaderData(request: Request, route: Route): Promise<{
- data: unknown;
- } | undefined>;
+ getLoaderData(request: Request, route: Route): Promise;
};
export {};
diff --git a/packages/expo-server/build/cjs/vendor/environment/common.js b/packages/expo-server/build/cjs/vendor/environment/common.js
index 24bc1a0e28a772..caa12fe39458bc 100644
--- a/packages/expo-server/build/cjs/vendor/environment/common.js
+++ b/packages/expo-server/build/cjs/vendor/environment/common.js
@@ -74,8 +74,7 @@ function createEnvironment(input) {
throw new Error(`Loader module not found at: ${route.loader}`);
}
const params = (0, matchers_1.parseParams)(request, route);
- const data = await loaderModule.loader({ params, request: new ImmutableRequest_1.ImmutableRequest(request) });
- return { data: data === undefined ? {} : data };
+ return loaderModule.loader({ params, request: new ImmutableRequest_1.ImmutableRequest(request) });
}
return {
async getRoutesManifest() {
@@ -87,9 +86,11 @@ function createEnvironment(input) {
if (renderer) {
let renderOptions;
try {
- const loaderResult = await executeLoader(request, route);
- if (loaderResult) {
- renderOptions = { loader: { data: loaderResult.data } };
+ const result = await executeLoader(request, route);
+ if (result !== undefined) {
+ const data = (0, matchers_1.isResponse)(result) ? await result.json() : result;
+ const normalizedData = data === undefined ? {} : data;
+ renderOptions = { loader: { data: normalizedData } };
}
return await renderer(request, renderOptions);
}
@@ -125,7 +126,12 @@ function createEnvironment(input) {
return mod;
},
async getLoaderData(request, route) {
- return executeLoader(request, route);
+ const result = await executeLoader(request, route);
+ if ((0, matchers_1.isResponse)(result)) {
+ return result;
+ }
+ const data = result === undefined ? {} : result;
+ return Response.json(data);
},
};
}
diff --git a/packages/expo-server/build/cjs/vendor/environment/common.js.map b/packages/expo-server/build/cjs/vendor/environment/common.js.map
index 1840c4228809fb..60bee3f9be73d2 100644
--- a/packages/expo-server/build/cjs/vendor/environment/common.js.map
+++ b/packages/expo-server/build/cjs/vendor/environment/common.js.map
@@ -1 +1 @@
-{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/vendor/environment/common.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/vendor/environment/common.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-server/build/cjs/vendor/environment/node.d.ts b/packages/expo-server/build/cjs/vendor/environment/node.d.ts
index 34764d6097746d..5675de2668ab02 100644
--- a/packages/expo-server/build/cjs/vendor/environment/node.d.ts
+++ b/packages/expo-server/build/cjs/vendor/environment/node.d.ts
@@ -8,9 +8,7 @@ export declare function createNodeEnv(params: NodeEnvParams): {
getHtml(request: Request, route: import("../../manifest").Route): Promise;
getApiRoute(route: import("../../manifest").Route): Promise;
getMiddleware(middleware: import("../../manifest").MiddlewareInfo): Promise;
- getLoaderData(request: Request, route: import("../../manifest").Route): Promise<{
- data: unknown;
- } | undefined>;
+ getLoaderData(request: Request, route: import("../../manifest").Route): Promise;
};
export declare function createNodeRequestScope(scopeDefinition: ScopeDefinition, params: NodeEnvParams): (fn: (request: Request) => Promise, request: Request) => Promise;
export {};
diff --git a/packages/expo-server/build/cjs/vendor/environment/workerd.d.ts b/packages/expo-server/build/cjs/vendor/environment/workerd.d.ts
index c487c455093e72..55c53f87f07d4f 100644
--- a/packages/expo-server/build/cjs/vendor/environment/workerd.d.ts
+++ b/packages/expo-server/build/cjs/vendor/environment/workerd.d.ts
@@ -8,9 +8,7 @@ export declare function createWorkerdEnv(params: WorkerdEnvParams): {
getHtml(request: Request, route: import("../../manifest").Route): Promise;
getApiRoute(route: import("../../manifest").Route): Promise;
getMiddleware(middleware: import("../../manifest").MiddlewareInfo): Promise;
- getLoaderData(request: Request, route: import("../../manifest").Route): Promise<{
- data: unknown;
- } | undefined>;
+ getLoaderData(request: Request, route: import("../../manifest").Route): Promise;
};
export interface ExecutionContext {
waitUntil?(promise: Promise): void;
diff --git a/packages/expo-server/build/mjs/vendor/abstract.d.ts b/packages/expo-server/build/mjs/vendor/abstract.d.ts
index dc9446e2252c3c..be2cdc116486c6 100644
--- a/packages/expo-server/build/mjs/vendor/abstract.d.ts
+++ b/packages/expo-server/build/mjs/vendor/abstract.d.ts
@@ -36,9 +36,7 @@ export interface RequestHandlerInput {
getRoutesManifest(): Promise;
getApiRoute(route: Route): Promise;
getMiddleware(route: MiddlewareInfo): Promise;
- getLoaderData(request: Request, route: Route): Promise<{
- data: unknown;
- } | undefined>;
+ getLoaderData(request: Request, route: Route): Promise;
}
export declare function createRequestHandler({ getRoutesManifest, getHtml, getApiRoute, getMiddleware, getLoaderData, beforeErrorResponse, beforeResponse, beforeHTMLResponse, beforeAPIResponse, }: RequestHandlerParams & RequestHandlerInput): (request: Request) => Promise;
export {};
diff --git a/packages/expo-server/build/mjs/vendor/abstract.js b/packages/expo-server/build/mjs/vendor/abstract.js
index 18f12626f0ad7c..e04d56ef97fa81 100644
--- a/packages/expo-server/build/mjs/vendor/abstract.js
+++ b/packages/expo-server/build/mjs/vendor/abstract.js
@@ -93,13 +93,7 @@ export function createRequestHandler({ getRoutesManifest, getHtml, getApiRoute,
// NOTE(@hassankhan): Relocate the request rewriting logic from here
url.pathname = matchedPath;
const loaderRequest = new Request(url, request);
- const loaderResult = await getLoaderData(loaderRequest, route);
- return createResponse('api', route, JSON.stringify(loaderResult?.data), {
- status: 200,
- headers: new Headers({
- 'Content-Type': 'application/json',
- }),
- });
+ return createResponseFrom('api', route, await getLoaderData(loaderRequest, route));
}
const html = await getHtml(request, route);
return respondHTML(html, route);
diff --git a/packages/expo-server/build/mjs/vendor/abstract.js.map b/packages/expo-server/build/mjs/vendor/abstract.js.map
index 1b10547e4a9db3..fa754cfc8643f4 100644
--- a/packages/expo-server/build/mjs/vendor/abstract.js.map
+++ b/packages/expo-server/build/mjs/vendor/abstract.js.map
@@ -1 +1 @@
-{"version":3,"file":"abstract.js","sourceRoot":"","sources":["../../../src/vendor/abstract.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"abstract.js","sourceRoot":"","sources":["../../../src/vendor/abstract.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-server/build/mjs/vendor/environment/common.d.ts b/packages/expo-server/build/mjs/vendor/environment/common.d.ts
index 5b777a66e8e601..5435517b26e6d6 100644
--- a/packages/expo-server/build/mjs/vendor/environment/common.d.ts
+++ b/packages/expo-server/build/mjs/vendor/environment/common.d.ts
@@ -9,8 +9,6 @@ export declare function createEnvironment(input: EnvironmentInput): {
getHtml(request: Request, route: Route): Promise;
getApiRoute(route: Route): Promise;
getMiddleware(middleware: MiddlewareInfo): Promise;
- getLoaderData(request: Request, route: Route): Promise<{
- data: unknown;
- } | undefined>;
+ getLoaderData(request: Request, route: Route): Promise;
};
export {};
diff --git a/packages/expo-server/build/mjs/vendor/environment/common.js b/packages/expo-server/build/mjs/vendor/environment/common.js
index d0996fb9695826..2321dae8ef8310 100644
--- a/packages/expo-server/build/mjs/vendor/environment/common.js
+++ b/packages/expo-server/build/mjs/vendor/environment/common.js
@@ -1,5 +1,5 @@
import { ImmutableRequest } from '../../ImmutableRequest';
-import { parseParams } from '../../utils/matchers';
+import { isResponse, parseParams } from '../../utils/matchers';
function initManifestRegExp(manifest) {
return {
...manifest,
@@ -71,8 +71,7 @@ export function createEnvironment(input) {
throw new Error(`Loader module not found at: ${route.loader}`);
}
const params = parseParams(request, route);
- const data = await loaderModule.loader({ params, request: new ImmutableRequest(request) });
- return { data: data === undefined ? {} : data };
+ return loaderModule.loader({ params, request: new ImmutableRequest(request) });
}
return {
async getRoutesManifest() {
@@ -84,9 +83,11 @@ export function createEnvironment(input) {
if (renderer) {
let renderOptions;
try {
- const loaderResult = await executeLoader(request, route);
- if (loaderResult) {
- renderOptions = { loader: { data: loaderResult.data } };
+ const result = await executeLoader(request, route);
+ if (result !== undefined) {
+ const data = isResponse(result) ? await result.json() : result;
+ const normalizedData = data === undefined ? {} : data;
+ renderOptions = { loader: { data: normalizedData } };
}
return await renderer(request, renderOptions);
}
@@ -122,7 +123,12 @@ export function createEnvironment(input) {
return mod;
},
async getLoaderData(request, route) {
- return executeLoader(request, route);
+ const result = await executeLoader(request, route);
+ if (isResponse(result)) {
+ return result;
+ }
+ const data = result === undefined ? {} : result;
+ return Response.json(data);
},
};
}
diff --git a/packages/expo-server/build/mjs/vendor/environment/common.js.map b/packages/expo-server/build/mjs/vendor/environment/common.js.map
index 9772a006e070b6..04bfe81ac98804 100644
--- a/packages/expo-server/build/mjs/vendor/environment/common.js.map
+++ b/packages/expo-server/build/mjs/vendor/environment/common.js.map
@@ -1 +1 @@
-{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/vendor/environment/common.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../src/vendor/environment/common.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-server/build/mjs/vendor/environment/node.d.ts b/packages/expo-server/build/mjs/vendor/environment/node.d.ts
index 34764d6097746d..5675de2668ab02 100644
--- a/packages/expo-server/build/mjs/vendor/environment/node.d.ts
+++ b/packages/expo-server/build/mjs/vendor/environment/node.d.ts
@@ -8,9 +8,7 @@ export declare function createNodeEnv(params: NodeEnvParams): {
getHtml(request: Request, route: import("../../manifest").Route): Promise;
getApiRoute(route: import("../../manifest").Route): Promise;
getMiddleware(middleware: import("../../manifest").MiddlewareInfo): Promise;
- getLoaderData(request: Request, route: import("../../manifest").Route): Promise<{
- data: unknown;
- } | undefined>;
+ getLoaderData(request: Request, route: import("../../manifest").Route): Promise;
};
export declare function createNodeRequestScope(scopeDefinition: ScopeDefinition, params: NodeEnvParams): (fn: (request: Request) => Promise, request: Request) => Promise;
export {};
diff --git a/packages/expo-server/build/mjs/vendor/environment/workerd.d.ts b/packages/expo-server/build/mjs/vendor/environment/workerd.d.ts
index c487c455093e72..55c53f87f07d4f 100644
--- a/packages/expo-server/build/mjs/vendor/environment/workerd.d.ts
+++ b/packages/expo-server/build/mjs/vendor/environment/workerd.d.ts
@@ -8,9 +8,7 @@ export declare function createWorkerdEnv(params: WorkerdEnvParams): {
getHtml(request: Request, route: import("../../manifest").Route): Promise;
getApiRoute(route: import("../../manifest").Route): Promise;
getMiddleware(middleware: import("../../manifest").MiddlewareInfo): Promise;
- getLoaderData(request: Request, route: import("../../manifest").Route): Promise<{
- data: unknown;
- } | undefined>;
+ getLoaderData(request: Request, route: import("../../manifest").Route): Promise;
};
export interface ExecutionContext {
waitUntil?(promise: Promise): void;
diff --git a/packages/expo-server/src/vendor/__tests__/abstract.test.ts b/packages/expo-server/src/vendor/__tests__/abstract.test.ts
index a93eb43d095a62..950a8a858edf73 100644
--- a/packages/expo-server/src/vendor/__tests__/abstract.test.ts
+++ b/packages/expo-server/src/vendor/__tests__/abstract.test.ts
@@ -69,7 +69,7 @@ describe(createRequestHandler, () => {
};
const loaderData = { message: 'Hello from loader', count: 42 };
- const getLoaderData = jest.fn(async () => ({ data: loaderData }));
+ const getLoaderData = jest.fn(async () => Response.json(loaderData));
const handler = createRequestHandler({
getRoutesManifest: jest.fn(async () => manifest),
@@ -106,7 +106,7 @@ describe(createRequestHandler, () => {
rewrites: [],
};
- const getLoaderData = jest.fn(async () => ({ data: {} }));
+ const getLoaderData = jest.fn(async () => Response.json({}));
const handler = createRequestHandler({
getRoutesManifest: jest.fn(async () => manifest),
@@ -155,7 +155,7 @@ describe(createRequestHandler, () => {
rewrites: [],
};
- const getLoaderData = jest.fn(async () => ({ data: 'loaded' }));
+ const getLoaderData = jest.fn(async () => Response.json('loaded'));
const handler = createRequestHandler({
getRoutesManifest: jest.fn(async () => manifest),
@@ -232,7 +232,7 @@ describe(createRequestHandler, () => {
};
const getHtml = jest.fn(async () => 'Second Page');
- const getLoaderData = jest.fn(async () => ({ data: 'loader' }));
+ const getLoaderData = jest.fn(async () => Response.json('loader'));
const handler = createRequestHandler({
getRoutesManifest: jest.fn(async () => manifest),
diff --git a/packages/expo-server/src/vendor/abstract.ts b/packages/expo-server/src/vendor/abstract.ts
index a08ca7332b33c7..cb22336b6d71f2 100644
--- a/packages/expo-server/src/vendor/abstract.ts
+++ b/packages/expo-server/src/vendor/abstract.ts
@@ -56,7 +56,7 @@ export interface RequestHandlerInput {
getRoutesManifest(): Promise;
getApiRoute(route: Route): Promise;
getMiddleware(route: MiddlewareInfo): Promise;
- getLoaderData(request: Request, route: Route): Promise<{ data: unknown } | undefined>;
+ getLoaderData(request: Request, route: Route): Promise;
}
export function createRequestHandler({
@@ -157,13 +157,7 @@ export function createRequestHandler({
// NOTE(@hassankhan): Relocate the request rewriting logic from here
url.pathname = matchedPath;
const loaderRequest = new Request(url, request);
- const loaderResult = await getLoaderData(loaderRequest, route);
- return createResponse('api', route, JSON.stringify(loaderResult?.data), {
- status: 200,
- headers: new Headers({
- 'Content-Type': 'application/json',
- }),
- });
+ return createResponseFrom('api', route, await getLoaderData(loaderRequest, route));
}
const html = await getHtml(request, route);
diff --git a/packages/expo-server/src/vendor/environment/__tests__/common.test.ts b/packages/expo-server/src/vendor/environment/__tests__/common.test.ts
index c4369bd79093d3..958b9a975b19bb 100644
--- a/packages/expo-server/src/vendor/environment/__tests__/common.test.ts
+++ b/packages/expo-server/src/vendor/environment/__tests__/common.test.ts
@@ -104,7 +104,7 @@ describe('getHtml', () => {
expect(input.loadModule).not.toHaveBeenCalled();
});
- it('returns null when static file not found', async () => {
+ it('returns `null` when static file not found', async () => {
const route = {
file: './missing.tsx',
page: '/missing',
@@ -410,7 +410,7 @@ describe('getMiddleware', () => {
expect(middleware).toBe(middlewareModule);
});
- it('returns null when default export is not a function', async () => {
+ it('returns `null` when default export is not a function', async () => {
const middlewareModule = { default: 'not a function' };
const input = createMockInput({
modules: {
@@ -425,7 +425,7 @@ describe('getMiddleware', () => {
expect(middleware).toBeNull();
});
- it('returns null when module has no default export', async () => {
+ it('returns `null` when module has no default export', async () => {
const middlewareModule = { named: jest.fn() };
const input = createMockInput({
modules: {
@@ -442,7 +442,7 @@ describe('getMiddleware', () => {
});
describe('getLoaderData', () => {
- it('returns loader data when route has loader', async () => {
+ it('returns `Response` with loader data when route has loader', async () => {
const loaderData = { userId: 123 };
const loaderModule = { loader: jest.fn().mockResolvedValue(loaderData) };
const input = createMockInput({
@@ -460,14 +460,15 @@ describe('getLoaderData', () => {
})
);
- expect(result).toEqual({ data: loaderData });
+ expect(result).toBeInstanceOf(Response);
+ expect(await result.json()).toEqual(loaderData);
expect(loaderModule.loader).toHaveBeenCalledWith({
params: {},
request: expect.any(ImmutableRequest),
});
});
- it('returns `undefined` when route has no loader', async () => {
+ it('returns `Response` with `{}` body when route has no loader', async () => {
const input = createMockInput();
const env = createEnvironment(input);
@@ -480,7 +481,8 @@ describe('getLoaderData', () => {
})
);
- expect(result).toBeUndefined();
+ expect(result).toBeInstanceOf(Response);
+ expect(await result.json()).toEqual({});
});
it('throws when loader module fails to load', async () => {
@@ -541,7 +543,8 @@ describe('getLoaderData', () => {
})
);
- expect(result).toEqual({ data: {} });
+ expect(result).toBeInstanceOf(Response);
+ expect(await result.json()).toEqual({});
});
it('passes through `null` loader result as `null`', async () => {
@@ -561,7 +564,34 @@ describe('getLoaderData', () => {
})
);
- expect(result).toEqual({ data: null });
+ expect(result).toBeInstanceOf(Response);
+ expect(await result.json()).toBeNull();
+ });
+
+ it('returns `Response` directly when loader returns `Response`', async () => {
+ const responseData = { test: 'response' };
+ const loaderResponse = Response.json(responseData, {
+ headers: { 'X-Custom': 'value' },
+ });
+ const loaderModule = { loader: jest.fn().mockResolvedValue(loaderResponse) };
+ const input = createMockInput({
+ modules: { '_expo/loaders/response-route.js': loaderModule },
+ });
+ const env = createEnvironment(input);
+
+ const result = await env.getLoaderData(
+ new Request('http://localhost/response-route'),
+ createMockRoute({
+ file: './response-route.tsx',
+ page: '/response-route',
+ namedRegex: new RegExp('^/response-route(?:/)?$'),
+ loader: '_expo/loaders/response-route.js',
+ })
+ );
+
+ expect(result).toBeInstanceOf(Response);
+ expect(result.headers.get('X-Custom')).toBe('value');
+ expect(await result.json()).toEqual(responseData);
});
});
diff --git a/packages/expo-server/src/vendor/environment/common.ts b/packages/expo-server/src/vendor/environment/common.ts
index f19e62a187087e..c8e94fd6b0e33e 100644
--- a/packages/expo-server/src/vendor/environment/common.ts
+++ b/packages/expo-server/src/vendor/environment/common.ts
@@ -1,7 +1,7 @@
import { ImmutableRequest } from '../../ImmutableRequest';
import type { Manifest, MiddlewareInfo, RawManifest, Route } from '../../manifest';
import type { LoaderModule, RenderOptions, ServerRenderModule, SsrRenderFn } from '../../rendering';
-import { parseParams } from '../../utils/matchers';
+import { isResponse, parseParams } from '../../utils/matchers';
function initManifestRegExp(manifest: RawManifest): Manifest {
return {
@@ -81,10 +81,7 @@ export function createEnvironment(input: EnvironmentInput) {
return ssrRenderer;
}
- async function executeLoader(
- request: Request,
- route: Route
- ): Promise<{ data: unknown } | undefined> {
+ async function executeLoader(request: Request, route: Route): Promise {
if (!route.loader) {
return undefined;
}
@@ -95,8 +92,7 @@ export function createEnvironment(input: EnvironmentInput) {
}
const params = parseParams(request, route);
- const data = await loaderModule.loader({ params, request: new ImmutableRequest(request) });
- return { data: data === undefined ? {} : data };
+ return loaderModule.loader({ params, request: new ImmutableRequest(request) });
}
return {
@@ -111,9 +107,11 @@ export function createEnvironment(input: EnvironmentInput) {
let renderOptions: RenderOptions | undefined;
try {
- const loaderResult = await executeLoader(request, route);
- if (loaderResult) {
- renderOptions = { loader: { data: loaderResult.data } };
+ const result = await executeLoader(request, route);
+ if (result !== undefined) {
+ const data = isResponse(result) ? await result.json() : result;
+ const normalizedData = data === undefined ? {} : data;
+ renderOptions = { loader: { data: normalizedData } };
}
return await renderer(request, renderOptions);
} catch (error) {
@@ -151,8 +149,15 @@ export function createEnvironment(input: EnvironmentInput) {
return mod;
},
- async getLoaderData(request: Request, route: Route): Promise<{ data: unknown } | undefined> {
- return executeLoader(request, route);
+ async getLoaderData(request: Request, route: Route): Promise {
+ const result = await executeLoader(request, route);
+
+ if (isResponse(result)) {
+ return result;
+ }
+
+ const data = result === undefined ? {} : result;
+ return Response.json(data);
},
};
}
diff --git a/packages/expo-ui/build/swift-ui/Button/index.d.ts b/packages/expo-ui/build/swift-ui/Button/index.d.ts
index 01595f053971a6..d80972849f3611 100644
--- a/packages/expo-ui/build/swift-ui/Button/index.d.ts
+++ b/packages/expo-ui/build/swift-ui/Button/index.d.ts
@@ -30,6 +30,10 @@ export type ButtonProps = {
* Only nested elements are supported, not plain strings.
*/
children?: React.ReactElement | React.ReactElement[];
+ /**
+ * Target identifier for the button, used for identifying which button was pressed in widgets and live activities.
+ */
+ target?: string;
} & CommonViewModifierProps;
/**
* Displays a native button component.
diff --git a/packages/expo-ui/build/swift-ui/Button/index.d.ts.map b/packages/expo-ui/build/swift-ui/Button/index.d.ts.map
index 4d44ab5dc67dc8..9a1711eba8be66 100644
--- a/packages/expo-ui/build/swift-ui/Button/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/Button/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Button/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;AAE9D,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB;;OAEG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;CACtD,GAAG,uBAAuB,CAAC;AAS5B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,+BAexC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Button/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;AAE9D,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB;;OAEG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;IACrD;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,uBAAuB,CAAC;AAS5B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,+BAexC"}
\ No newline at end of file
diff --git a/packages/expo-ui/ios/Button/ButtonProps.swift b/packages/expo-ui/ios/Button/ButtonProps.swift
index 6b7fd8a1ec099b..700353f80b138c 100644
--- a/packages/expo-ui/ios/Button/ButtonProps.swift
+++ b/packages/expo-ui/ios/Button/ButtonProps.swift
@@ -3,12 +3,12 @@
import SwiftUI
import ExpoModulesCore
-internal enum ButtonRole: String, Enumerable {
+public enum ButtonRole: String, Enumerable {
case `default`
case destructive
case cancel
- func toNativeRole() -> SwiftUI.ButtonRole? {
+ public func toNativeRole() -> SwiftUI.ButtonRole? {
switch self {
case .default:
return nil
@@ -20,9 +20,9 @@ internal enum ButtonRole: String, Enumerable {
}
}
-public final class ButtonProps: UIBaseViewProps, Observable {
- @Field var label: String?
- @Field var systemImage: String?
- @Field var role: ButtonRole?
+open class ButtonProps: UIBaseViewProps, Observable {
+ @Field public var label: String?
+ @Field public var systemImage: String?
+ @Field public var role: ButtonRole?
var onButtonPress = EventDispatcher()
}
diff --git a/packages/expo-ui/ios/TextView.swift b/packages/expo-ui/ios/TextView.swift
index 63d5455ddf5180..ec9819ae5d653e 100644
--- a/packages/expo-ui/ios/TextView.swift
+++ b/packages/expo-ui/ios/TextView.swift
@@ -4,7 +4,7 @@ import SwiftUI
import ExpoModulesCore
public final class TextViewProps: UIBaseViewProps {
- @Field var text: String = ""
+ @Field public var text: String = ""
// Override default frame alignment for text views
override var defaultFrameAlignment: Alignment { .leading }
diff --git a/packages/expo-ui/src/swift-ui/Button/index.tsx b/packages/expo-ui/src/swift-ui/Button/index.tsx
index ec0a46d86c60c6..ad67f23255bc96 100644
--- a/packages/expo-ui/src/swift-ui/Button/index.tsx
+++ b/packages/expo-ui/src/swift-ui/Button/index.tsx
@@ -36,6 +36,10 @@ export type ButtonProps = {
* Only nested elements are supported, not plain strings.
*/
children?: React.ReactElement | React.ReactElement[];
+ /**
+ * Target identifier for the button, used for identifying which button was pressed in widgets and live activities.
+ */
+ target?: string;
} & CommonViewModifierProps;
type NativeButtonProps = Omit & ViewEvent<'onButtonPress', void>;
diff --git a/packages/expo-widgets/app.plugin.js b/packages/expo-widgets/app.plugin.js
new file mode 100644
index 00000000000000..d15b65ed02f220
--- /dev/null
+++ b/packages/expo-widgets/app.plugin.js
@@ -0,0 +1 @@
+module.exports = require('./plugin/build/index');
diff --git a/packages/expo-widgets/package.json b/packages/expo-widgets/package.json
index 144e973e4a6510..babcdc3b19ccdf 100644
--- a/packages/expo-widgets/package.json
+++ b/packages/expo-widgets/package.json
@@ -6,6 +6,7 @@
"types": "build/index.d.ts",
"scripts": {
"build": "expo-module build",
+ "build:plugin": "expo-module build plugin",
"clean": "expo-module clean",
"lint": "expo-module lint",
"test": "expo-module test",
@@ -29,7 +30,9 @@
"author": "650 Industries, Inc.",
"license": "MIT",
"homepage": "https://docs.expo.dev/versions/latest/sdk/widgets/",
- "dependencies": {},
+ "dependencies": {
+ "@expo/plist": "^0.4.7"
+ },
"devDependencies": {
"expo-module-scripts": "^5.0.7"
},
diff --git a/packages/expo-widgets/plugin/build/index.d.ts b/packages/expo-widgets/plugin/build/index.d.ts
new file mode 100644
index 00000000000000..55befa750f150e
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/index.d.ts
@@ -0,0 +1,11 @@
+import { ConfigPlugin } from 'expo/config-plugins';
+import { WidgetConfig } from './types/WidgetConfig.type';
+type ExpoWidgetsConfigPluginProps = {
+ bundleIdentifier?: string;
+ groupIdentifier?: string;
+ enablePushNotifications?: boolean;
+ frequentUpdates?: boolean;
+ widgets?: WidgetConfig[];
+};
+declare const _default: ConfigPlugin;
+export default _default;
diff --git a/packages/expo-widgets/plugin/build/index.js b/packages/expo-widgets/plugin/build/index.js
new file mode 100644
index 00000000000000..82fc65077acda3
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/index.js
@@ -0,0 +1,50 @@
+"use strict";
+var __importDefault = (this && this.__importDefault) || function (mod) {
+ return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const config_plugins_1 = require("expo/config-plugins");
+const withAppGroupEntitlements_1 = __importDefault(require("./withAppGroupEntitlements"));
+const withAppInfoPlist_1 = __importDefault(require("./withAppInfoPlist"));
+const withPodsLinking_1 = __importDefault(require("./withPodsLinking"));
+const withPushNotifications_1 = __importDefault(require("./withPushNotifications"));
+const withWidgetSourceFiles_1 = __importDefault(require("./withWidgetSourceFiles"));
+const withTargetXcodeProject_1 = __importDefault(require("./xcode/withTargetXcodeProject"));
+const pkg = require('expo-widgets/package.json');
+const withWidgets = (config, props) => {
+ const deploymentTarget = '16.2';
+ const targetName = 'ExpoWidgetsTarget';
+ let bundleIdentifier = props.bundleIdentifier;
+ if (!bundleIdentifier) {
+ bundleIdentifier = `${config.ios?.bundleIdentifier}.${targetName}`;
+ console.log(`No bundleIdentifier provided, fallback to: ${bundleIdentifier}`);
+ }
+ let groupIdentifier = props.groupIdentifier;
+ if (!groupIdentifier) {
+ if (!config.ios?.bundleIdentifier) {
+ throw new Error('iOS bundle identifier is required. Please set `ios.bundleIdentifier` in `app.json` or `app.config.js`');
+ }
+ groupIdentifier = `group.${config.ios.bundleIdentifier}`;
+ console.log(`No groupIdentifier provided, fallback to: ${groupIdentifier}`);
+ }
+ let widgets = props.widgets;
+ if (!widgets) {
+ widgets = [];
+ }
+ const enablePushNotifications = props.enablePushNotifications ?? false;
+ const frequentUpdates = props.frequentUpdates ?? false;
+ let sharedFiles = [];
+ const setFiles = (files) => {
+ sharedFiles = [...sharedFiles, ...files];
+ };
+ const getFileUris = () => sharedFiles;
+ return (0, config_plugins_1.withPlugins)(config, [
+ [withPodsLinking_1.default, { targetName }],
+ [withWidgetSourceFiles_1.default, { targetName, widgets, groupIdentifier, onFilesGenerated: setFiles }],
+ [withAppInfoPlist_1.default, { frequentUpdates, groupIdentifier }],
+ [withPushNotifications_1.default, { enablePushNotifications }],
+ [withAppGroupEntitlements_1.default, { groupIdentifier }],
+ [withTargetXcodeProject_1.default, { targetName, bundleIdentifier, deploymentTarget, getFileUris }],
+ ]);
+};
+exports.default = (0, config_plugins_1.createRunOncePlugin)(withWidgets, pkg.name, pkg.version);
diff --git a/packages/expo-widgets/plugin/build/types/WidgetConfig.type.d.ts b/packages/expo-widgets/plugin/build/types/WidgetConfig.type.d.ts
new file mode 100644
index 00000000000000..7cb740dc8f03a6
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/types/WidgetConfig.type.d.ts
@@ -0,0 +1,7 @@
+import { WidgetFamily } from './WidgetFamily.type';
+export type WidgetConfig = {
+ name: string;
+ supportedFamilies: WidgetFamily[];
+ displayName: string;
+ description: string;
+};
diff --git a/packages/expo-widgets/plugin/build/types/WidgetConfig.type.js b/packages/expo-widgets/plugin/build/types/WidgetConfig.type.js
new file mode 100644
index 00000000000000..c8ad2e549bdc68
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/types/WidgetConfig.type.js
@@ -0,0 +1,2 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
diff --git a/packages/expo-widgets/plugin/build/types/WidgetFamily.type.d.ts b/packages/expo-widgets/plugin/build/types/WidgetFamily.type.d.ts
new file mode 100644
index 00000000000000..0cbb0ee7c2894a
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/types/WidgetFamily.type.d.ts
@@ -0,0 +1,9 @@
+export declare enum WidgetFamily {
+ systemSmall = "systemSmall",
+ systemMedium = "systemMedium",
+ systemLarge = "systemLarge",
+ systemExtraLarge = "systemExtraLarge",
+ accessoryCircular = "accessoryCircular",
+ accessoryRectangular = "accessoryRectangular",
+ accessoryInline = "accessoryInline"
+}
diff --git a/packages/expo-widgets/plugin/build/types/WidgetFamily.type.js b/packages/expo-widgets/plugin/build/types/WidgetFamily.type.js
new file mode 100644
index 00000000000000..f4ca103a09f30c
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/types/WidgetFamily.type.js
@@ -0,0 +1,13 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.WidgetFamily = void 0;
+var WidgetFamily;
+(function (WidgetFamily) {
+ WidgetFamily["systemSmall"] = "systemSmall";
+ WidgetFamily["systemMedium"] = "systemMedium";
+ WidgetFamily["systemLarge"] = "systemLarge";
+ WidgetFamily["systemExtraLarge"] = "systemExtraLarge";
+ WidgetFamily["accessoryCircular"] = "accessoryCircular";
+ WidgetFamily["accessoryRectangular"] = "accessoryRectangular";
+ WidgetFamily["accessoryInline"] = "accessoryInline";
+})(WidgetFamily || (exports.WidgetFamily = WidgetFamily = {}));
diff --git a/packages/expo-widgets/plugin/build/withAppGroupEntitlements.d.ts b/packages/expo-widgets/plugin/build/withAppGroupEntitlements.d.ts
new file mode 100644
index 00000000000000..0b51a45f51e5d8
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withAppGroupEntitlements.d.ts
@@ -0,0 +1,6 @@
+import { ConfigPlugin } from 'expo/config-plugins';
+type AppGroupEntitlementsProps = {
+ groupIdentifier: string;
+};
+declare const withAppGroupEntitlements: ConfigPlugin;
+export default withAppGroupEntitlements;
diff --git a/packages/expo-widgets/plugin/build/withAppGroupEntitlements.js b/packages/expo-widgets/plugin/build/withAppGroupEntitlements.js
new file mode 100644
index 00000000000000..3af26528830bf9
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withAppGroupEntitlements.js
@@ -0,0 +1,24 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const config_plugins_1 = require("expo/config-plugins");
+const withAppGroupEntitlements = (config, props) => (0, config_plugins_1.withEntitlementsPlist)(config, (config) => {
+ config.ios = {
+ ...config.ios,
+ entitlements: _addApplicationGroupsEntitlement(config.ios?.entitlements ?? {}, props.groupIdentifier),
+ };
+ return config;
+});
+exports.default = withAppGroupEntitlements;
+function _addApplicationGroupsEntitlement(entitlements, groupIdentifier) {
+ if (!groupIdentifier) {
+ return entitlements;
+ }
+ const existingApplicationGroups = entitlements['com.apple.security.application-groups'] ?? [];
+ if (!existingApplicationGroups.includes(groupIdentifier)) {
+ entitlements['com.apple.security.application-groups'] = [
+ groupIdentifier,
+ ...existingApplicationGroups,
+ ];
+ }
+ return entitlements;
+}
diff --git a/packages/expo-widgets/plugin/build/withAppInfoPlist.d.ts b/packages/expo-widgets/plugin/build/withAppInfoPlist.d.ts
new file mode 100644
index 00000000000000..2c6b1fd3595d05
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withAppInfoPlist.d.ts
@@ -0,0 +1,7 @@
+import { ConfigPlugin } from 'expo/config-plugins';
+type withAppInfoPlistProps = {
+ frequentUpdates: boolean;
+ groupIdentifier: string;
+};
+declare const withAppInfoPlist: ConfigPlugin;
+export default withAppInfoPlist;
diff --git a/packages/expo-widgets/plugin/build/withAppInfoPlist.js b/packages/expo-widgets/plugin/build/withAppInfoPlist.js
new file mode 100644
index 00000000000000..7ae1a433620344
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withAppInfoPlist.js
@@ -0,0 +1,11 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const config_plugins_1 = require("expo/config-plugins");
+const withAppInfoPlist = (config, { frequentUpdates, groupIdentifier }) => (0, config_plugins_1.withInfoPlist)(config, (config) => {
+ const infoPlist = config.modResults;
+ infoPlist.NSSupportsLiveActivities = true;
+ infoPlist.NSSupportsLiveActivitiesFrequentUpdates = frequentUpdates;
+ infoPlist.ExpoWidgetsAppGroupIdentifier = groupIdentifier;
+ return config;
+});
+exports.default = withAppInfoPlist;
diff --git a/packages/expo-widgets/plugin/build/withPodsLinking.d.ts b/packages/expo-widgets/plugin/build/withPodsLinking.d.ts
new file mode 100644
index 00000000000000..2621c4b83d4fbe
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withPodsLinking.d.ts
@@ -0,0 +1,6 @@
+import { ConfigPlugin } from 'expo/config-plugins';
+interface PodsLinkingProps {
+ targetName: string;
+}
+declare const withPodsLinking: ConfigPlugin;
+export default withPodsLinking;
diff --git a/packages/expo-widgets/plugin/build/withPodsLinking.js b/packages/expo-widgets/plugin/build/withPodsLinking.js
new file mode 100644
index 00000000000000..26b76aaa4c3810
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withPodsLinking.js
@@ -0,0 +1,101 @@
+"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;
+ };
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
+const config_plugins_1 = require("expo/config-plugins");
+const fs = __importStar(require("fs"));
+const path = __importStar(require("path"));
+const withPodsLinking = (config, { targetName }) => (0, config_plugins_1.withDangerousMod)(config, [
+ 'ios',
+ async (config) => {
+ const podsFilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile');
+ let podfileContent = fs.readFileSync(podsFilePath, 'utf8');
+ if (podfileContent.includes(`target "${targetName}" do`)) {
+ return config;
+ }
+ podfileContent += podfileExpoWidgetsLinking(targetName);
+ // expo_widgets_post_install hook
+ if (!podfileContent.includes('post_install do |installer|')) {
+ podfileContent += `
+post_install do |installer|
+ expo_widgets_post_install(installer)
+end
+`;
+ }
+ else {
+ podfileContent = podfileContent.replace(/post_install do \|installer\|([\s\S]*?)end\s*$/m, (match, p1) => {
+ return `post_install do |installer|
+ expo_widgets_post_install(installer)
+${p1}end
+`;
+ });
+ }
+ fs.writeFileSync(podsFilePath, podfileContent, 'utf8');
+ return config;
+ },
+]);
+exports.default = withPodsLinking;
+const podfileExpoWidgetsLinking = (targetName) => `
+require File.join(File.dirname(\`node --print "require.resolve('expo-widgets/package.json')"\`), "scripts/autolinking")
+target "${targetName}" do
+ use_expo_modules_widgets!
+
+ if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
+ config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
+ else
+ config_command = [
+ 'node',
+ '--no-warnings',
+ '--eval',
+ "require(require.resolve('expo-modules-autolinking', { paths: [require.resolve('expo/package.json')] }))(process.argv.slice(1))",
+ 'react-native-config',
+ '--json',
+ '--platform',
+ 'ios'
+ ]
+ end
+
+ config = use_expo_native_module!(config_command)
+
+ use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
+ use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
+
+ use_react_native!(
+ :path => config[:reactNativePath],
+ :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
+ :app_path => "#{Pod::Config.instance.installation_root}/..",
+ :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
+ )
+end
+`;
diff --git a/packages/expo-widgets/plugin/build/withPushNotifications.d.ts b/packages/expo-widgets/plugin/build/withPushNotifications.d.ts
new file mode 100644
index 00000000000000..68eb2d5e1b6890
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withPushNotifications.d.ts
@@ -0,0 +1,6 @@
+import { ConfigPlugin } from 'expo/config-plugins';
+type PushNotificationProps = {
+ enablePushNotifications: boolean;
+};
+declare const withPushNotifications: ConfigPlugin;
+export default withPushNotifications;
diff --git a/packages/expo-widgets/plugin/build/withPushNotifications.js b/packages/expo-widgets/plugin/build/withPushNotifications.js
new file mode 100644
index 00000000000000..05cd7fb3dd509e
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withPushNotifications.js
@@ -0,0 +1,11 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const config_plugins_1 = require("expo/config-plugins");
+const withPushNotifications = (config, props) => (0, config_plugins_1.withInfoPlist)((0, config_plugins_1.withEntitlementsPlist)(config, (mod) => {
+ mod.modResults['aps-environment'] = 'development';
+ return mod;
+}), (mod) => {
+ mod.modResults['ExpoLiveActivity_EnablePushNotifications'] = props.enablePushNotifications;
+ return mod;
+});
+exports.default = withPushNotifications;
diff --git a/packages/expo-widgets/plugin/build/withWidgetSourceFiles.d.ts b/packages/expo-widgets/plugin/build/withWidgetSourceFiles.d.ts
new file mode 100644
index 00000000000000..995d794b1edbef
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withWidgetSourceFiles.d.ts
@@ -0,0 +1,10 @@
+import { ConfigPlugin } from 'expo/config-plugins';
+import { WidgetConfig } from './types/WidgetConfig.type';
+type WidgetSourceFilesProps = {
+ targetName: string;
+ groupIdentifier: string;
+ widgets: WidgetConfig[];
+ onFilesGenerated: (files: string[]) => void;
+};
+declare const withWidgetSourceFiles: ConfigPlugin;
+export default withWidgetSourceFiles;
diff --git a/packages/expo-widgets/plugin/build/withWidgetSourceFiles.js b/packages/expo-widgets/plugin/build/withWidgetSourceFiles.js
new file mode 100644
index 00000000000000..faa12c2e7da55e
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/withWidgetSourceFiles.js
@@ -0,0 +1,132 @@
+"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 plist_1 = __importDefault(require("@expo/plist"));
+const config_plugins_1 = require("expo/config-plugins");
+const fs = __importStar(require("fs"));
+const path = __importStar(require("path"));
+const withWidgetSourceFiles = (config, { widgets, targetName, onFilesGenerated, groupIdentifier }) => (0, config_plugins_1.withDangerousMod)(config, [
+ 'ios',
+ async (config) => {
+ const projectRoot = config.modRequest.platformProjectRoot;
+ const targetDirectory = path.join(projectRoot, targetName);
+ if (fs.existsSync(targetDirectory)) {
+ fs.rmSync(targetDirectory, { recursive: true, force: true });
+ }
+ if (!fs.existsSync(targetDirectory)) {
+ fs.mkdirSync(targetDirectory, { recursive: true });
+ }
+ const entitlementsPath = path.join(targetDirectory, `${targetName}.entitlements`);
+ const entitlementsContent = {
+ 'com.apple.security.application-groups': [groupIdentifier],
+ };
+ fs.writeFileSync(entitlementsPath, plist_1.default.build(entitlementsContent));
+ const infoPlistPath = createInfoPlist(groupIdentifier, targetDirectory);
+ const indexSwiftPath = createIndexSwift(widgets, targetDirectory);
+ const widgetSwiftPaths = widgets.map((widget) => createWidgetSwift(widget, targetDirectory));
+ onFilesGenerated([entitlementsPath, infoPlistPath, indexSwiftPath, ...widgetSwiftPaths]);
+ return config;
+ },
+]);
+const createIndexSwift = (widgets, targetPath) => {
+ const indexFilePath = path.join(targetPath, `index.swift`);
+ const numberOfWidgets = widgets.length;
+ const numberOfBundles = Math.ceil(numberOfWidgets / 4);
+ let output = `import WidgetKit
+import SwiftUI
+import ExpoWidgets
+`;
+ for (let i = 0; i < numberOfBundles; i++) {
+ const start = i * 4;
+ const end = Math.min(start + 4, numberOfWidgets);
+ const widgetChunk = widgets.slice(start, end);
+ const isLastChunk = i === numberOfBundles - 1;
+ output += addIndexSwiftChunk(widgetChunk, i, isLastChunk);
+ }
+ fs.writeFileSync(indexFilePath, output);
+ return indexFilePath;
+};
+const createWidgetSwift = (widget, targetPath) => {
+ const widgetFilePath = path.join(targetPath, `${widget.name}.swift`);
+ fs.writeFileSync(widgetFilePath, widgetSwift(widget));
+ return widgetFilePath;
+};
+const createInfoPlist = (groupIdentifier, targetPath) => {
+ const infoPlistPath = `${targetPath}/Info.plist`;
+ fs.writeFileSync(infoPlistPath, infoPlist(groupIdentifier));
+ return infoPlistPath;
+};
+const addIndexSwiftChunk = (widgets, index, isLastChunk) => `
+${index === 0 ? '@main' : ''}
+struct ExportWidgets${index}: WidgetBundle {
+ var body: some Widget {
+ ${widgets.map((widget) => `${widget.name}()`).join('\n\t\t')}
+ ${!isLastChunk ? `ExportWidgets${index + 1}().body` : `WidgetLiveActivity()`}
+ }
+}`;
+const widgetSwift = (widget) => `import WidgetKit
+import SwiftUI
+import ExpoWidgets
+
+struct ${widget.name}: Widget {
+ let name: String = "${widget.name}"
+
+ var body: some WidgetConfiguration {
+ StaticConfiguration(kind: name, provider: WidgetsTimelineProvider(name: name)) { entry in
+ WidgetsEntryView(entry: entry)
+ }
+ .configurationDisplayName("${widget.displayName}")
+ .description("${widget.description}")
+ .supportedFamilies([.${widget.supportedFamilies.join(', .')}])
+ }
+}`;
+const infoPlist = (groupIdentifier) => `
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.widgetkit-extension
+
+ ExpoWidgetsAppGroupIdentifier
+ ${groupIdentifier}
+
+
+`;
+exports.default = withWidgetSourceFiles;
diff --git a/packages/expo-widgets/plugin/build/xcode/addBuildPhases.d.ts b/packages/expo-widgets/plugin/build/xcode/addBuildPhases.d.ts
new file mode 100644
index 00000000000000..b8245b546cfc79
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addBuildPhases.d.ts
@@ -0,0 +1,12 @@
+import { XcodeProject } from 'expo/config-plugins';
+export declare function addBuildPhases(xcodeProject: XcodeProject, { targetUuid, groupName, productFile, widgetFiles, }: {
+ targetUuid: string;
+ groupName: string;
+ productFile: {
+ uuid: string;
+ target: string;
+ basename: string;
+ group: string;
+ };
+ widgetFiles: string[];
+}): void;
diff --git a/packages/expo-widgets/plugin/build/xcode/addBuildPhases.js b/packages/expo-widgets/plugin/build/xcode/addBuildPhases.js
new file mode 100644
index 00000000000000..5f15a338efcbec
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addBuildPhases.js
@@ -0,0 +1,54 @@
+"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;
+ };
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.addBuildPhases = addBuildPhases;
+const util = __importStar(require("util"));
+function addBuildPhases(xcodeProject, { targetUuid, groupName, productFile, widgetFiles, }) {
+ const buildPath = `""`;
+ const folderType = 'app_extension';
+ // Sources build phase
+ xcodeProject.addBuildPhase([...widgetFiles], 'PBXSourcesBuildPhase', groupName, targetUuid, folderType, buildPath);
+ // Copy files build phase
+ xcodeProject.addBuildPhase([], 'PBXCopyFilesBuildPhase', groupName, xcodeProject.getFirstTarget().uuid, folderType, buildPath);
+ xcodeProject
+ .buildPhaseObject('PBXCopyFilesBuildPhase', groupName, productFile.target)
+ .files.push({
+ value: productFile.uuid,
+ comment: util.format('%s in %s', productFile.basename, productFile.group), // longComment(file);
+ });
+ xcodeProject.addToPbxBuildFileSection(productFile);
+ // Frameworks build phase
+ xcodeProject.addBuildPhase([], 'PBXFrameworksBuildPhase', groupName, targetUuid, folderType, buildPath);
+}
diff --git a/packages/expo-widgets/plugin/build/xcode/addPbxGroup.d.ts b/packages/expo-widgets/plugin/build/xcode/addPbxGroup.d.ts
new file mode 100644
index 00000000000000..23e455f958be40
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addPbxGroup.d.ts
@@ -0,0 +1,5 @@
+import { XcodeProject } from 'expo/config-plugins';
+export declare function addPbxGroup(xcodeProject: XcodeProject, { targetName, widgetFiles, }: {
+ targetName: string;
+ widgetFiles: string[];
+}): void;
diff --git a/packages/expo-widgets/plugin/build/xcode/addPbxGroup.js b/packages/expo-widgets/plugin/build/xcode/addPbxGroup.js
new file mode 100644
index 00000000000000..85f20b742d3757
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addPbxGroup.js
@@ -0,0 +1,16 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.addPbxGroup = addPbxGroup;
+function addPbxGroup(xcodeProject, { targetName, widgetFiles, }) {
+ // Add PBX group
+ const { uuid: pbxGroupUuid } = xcodeProject.addPbxGroup([...widgetFiles, `${targetName}.entitlements`], targetName, targetName);
+ // Add PBXGroup to top level group
+ const groups = xcodeProject.hash.project.objects['PBXGroup'];
+ if (pbxGroupUuid) {
+ Object.keys(groups).forEach(function (key) {
+ if (groups[key].name === undefined && groups[key].path === undefined) {
+ xcodeProject.addToPbxGroup(pbxGroupUuid, key);
+ }
+ });
+ }
+}
diff --git a/packages/expo-widgets/plugin/build/xcode/addProductFile.d.ts b/packages/expo-widgets/plugin/build/xcode/addProductFile.d.ts
new file mode 100644
index 00000000000000..c2d175ab33f340
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addProductFile.d.ts
@@ -0,0 +1,7 @@
+import { XcodeProject } from 'expo/config-plugins';
+type AddProductFileProps = {
+ targetName: string;
+ groupName: string;
+};
+export declare function addProductFile(xcodeProject: XcodeProject, { targetName, groupName }: AddProductFileProps): any;
+export {};
diff --git a/packages/expo-widgets/plugin/build/xcode/addProductFile.js b/packages/expo-widgets/plugin/build/xcode/addProductFile.js
new file mode 100644
index 00000000000000..5f43d518503f9a
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addProductFile.js
@@ -0,0 +1,17 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.addProductFile = addProductFile;
+function addProductFile(xcodeProject, { targetName, groupName }) {
+ const options = {
+ basename: `${targetName}.appex`,
+ group: groupName,
+ explicitFileType: 'wrapper.app-extension',
+ settings: {
+ ATTRIBUTES: ['RemoveHeadersOnCopy'],
+ },
+ includeInIndex: 0,
+ path: `${targetName}.appex`,
+ sourceTree: 'BUILT_PRODUCTS_DIR',
+ };
+ return xcodeProject.addProductFile(targetName, options);
+}
diff --git a/packages/expo-widgets/plugin/build/xcode/addTargetDependency.d.ts b/packages/expo-widgets/plugin/build/xcode/addTargetDependency.d.ts
new file mode 100644
index 00000000000000..bd1a00160c2f56
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addTargetDependency.d.ts
@@ -0,0 +1,4 @@
+import { XcodeProject } from 'expo/config-plugins';
+export declare function addTargetDependency(xcodeProject: XcodeProject, target: {
+ uuid: string;
+}): void;
diff --git a/packages/expo-widgets/plugin/build/xcode/addTargetDependency.js b/packages/expo-widgets/plugin/build/xcode/addTargetDependency.js
new file mode 100644
index 00000000000000..f4f38b692b22e7
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addTargetDependency.js
@@ -0,0 +1,12 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.addTargetDependency = addTargetDependency;
+function addTargetDependency(xcodeProject, target) {
+ if (!xcodeProject.hash.project.objects['PBXTargetDependency']) {
+ xcodeProject.hash.project.objects['PBXTargetDependency'] = {};
+ }
+ if (!xcodeProject.hash.project.objects['PBXContainerItemProxy']) {
+ xcodeProject.hash.project.objects['PBXContainerItemProxy'] = {};
+ }
+ xcodeProject.addTargetDependency(xcodeProject.getFirstTarget().uuid, [target.uuid]);
+}
diff --git a/packages/expo-widgets/plugin/build/xcode/addToPbxNativeTargetSection.d.ts b/packages/expo-widgets/plugin/build/xcode/addToPbxNativeTargetSection.d.ts
new file mode 100644
index 00000000000000..226fc4732742a6
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addToPbxNativeTargetSection.d.ts
@@ -0,0 +1,24 @@
+import { XcodeProject } from 'expo/config-plugins';
+export declare function addToPbxNativeTargetSection(xcodeProject: XcodeProject, { targetName, targetUuid, productFile, xCConfigurationList, }: {
+ targetName: string;
+ targetUuid: string;
+ productFile: {
+ fileRef: string;
+ };
+ xCConfigurationList: {
+ uuid: string;
+ };
+}): {
+ uuid: string;
+ pbxNativeTarget: {
+ isa: string;
+ name: string;
+ productName: string;
+ productReference: string;
+ productType: string;
+ buildConfigurationList: string;
+ buildPhases: never[];
+ buildRules: never[];
+ dependencies: never[];
+ };
+};
diff --git a/packages/expo-widgets/plugin/build/xcode/addToPbxNativeTargetSection.js b/packages/expo-widgets/plugin/build/xcode/addToPbxNativeTargetSection.js
new file mode 100644
index 00000000000000..3e62353ea91e15
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addToPbxNativeTargetSection.js
@@ -0,0 +1,21 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.addToPbxNativeTargetSection = addToPbxNativeTargetSection;
+function addToPbxNativeTargetSection(xcodeProject, { targetName, targetUuid, productFile, xCConfigurationList, }) {
+ const target = {
+ uuid: targetUuid,
+ pbxNativeTarget: {
+ isa: 'PBXNativeTarget',
+ name: targetName,
+ productName: targetName,
+ productReference: productFile.fileRef,
+ productType: `"com.apple.product-type.app-extension"`,
+ buildConfigurationList: xCConfigurationList.uuid,
+ buildPhases: [],
+ buildRules: [],
+ dependencies: [],
+ },
+ };
+ xcodeProject.addToPbxNativeTargetSection(target);
+ return target;
+}
diff --git a/packages/expo-widgets/plugin/build/xcode/addToPbxProjectSection.d.ts b/packages/expo-widgets/plugin/build/xcode/addToPbxProjectSection.d.ts
new file mode 100644
index 00000000000000..6d55a56baa2b2c
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addToPbxProjectSection.d.ts
@@ -0,0 +1,4 @@
+import { XcodeProject } from 'expo/config-plugins';
+export declare function addToPbxProjectSection(xcodeProject: XcodeProject, target: {
+ uuid: string;
+}): void;
diff --git a/packages/expo-widgets/plugin/build/xcode/addToPbxProjectSection.js b/packages/expo-widgets/plugin/build/xcode/addToPbxProjectSection.js
new file mode 100644
index 00000000000000..1bd65ff9fcbe39
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addToPbxProjectSection.js
@@ -0,0 +1,14 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.addToPbxProjectSection = addToPbxProjectSection;
+function addToPbxProjectSection(xcodeProject, target) {
+ xcodeProject.addToPbxProjectSection(target);
+ // Add target attributes to project section
+ if (!xcodeProject.pbxProjectSection()[xcodeProject.getFirstProject().uuid].attributes
+ .TargetAttributes) {
+ xcodeProject.pbxProjectSection()[xcodeProject.getFirstProject().uuid].attributes.TargetAttributes = {};
+ }
+ xcodeProject.pbxProjectSection()[xcodeProject.getFirstProject().uuid].attributes.TargetAttributes[target.uuid] = {
+ LastSwiftMigration: 1250,
+ };
+}
diff --git a/packages/expo-widgets/plugin/build/xcode/addXCConfigurationList.d.ts b/packages/expo-widgets/plugin/build/xcode/addXCConfigurationList.d.ts
new file mode 100644
index 00000000000000..bb42cd2e513a95
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addXCConfigurationList.d.ts
@@ -0,0 +1,10 @@
+import { XcodeProject } from 'expo/config-plugins';
+interface AddXCConfigurationListProps {
+ targetName: string;
+ currentProjectVersion: string;
+ bundleIdentifier: string;
+ deploymentTarget: string;
+ marketingVersion?: string;
+}
+export declare function addXCConfigurationList(xcodeProject: XcodeProject, props: AddXCConfigurationListProps): any;
+export {};
diff --git a/packages/expo-widgets/plugin/build/xcode/addXCConfigurationList.js b/packages/expo-widgets/plugin/build/xcode/addXCConfigurationList.js
new file mode 100644
index 00000000000000..032982b06b4a24
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/addXCConfigurationList.js
@@ -0,0 +1,39 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.addXCConfigurationList = addXCConfigurationList;
+function addXCConfigurationList(xcodeProject, props) {
+ const commonBuildSettings = {
+ PRODUCT_NAME: `"$(TARGET_NAME)"`,
+ SWIFT_VERSION: '5.0',
+ TARGETED_DEVICE_FAMILY: `"1,2"`,
+ INFOPLIST_FILE: `${props.targetName}/Info.plist`,
+ CURRENT_PROJECT_VERSION: `"${props.currentProjectVersion}"`,
+ IPHONEOS_DEPLOYMENT_TARGET: `"${props.deploymentTarget}"`,
+ PRODUCT_BUNDLE_IDENTIFIER: `"${props.bundleIdentifier}"`,
+ GENERATE_INFOPLIST_FILE: `"YES"`,
+ INFOPLIST_KEY_CFBundleDisplayName: props.targetName,
+ INFOPLIST_KEY_NSHumanReadableCopyright: `""`,
+ MARKETING_VERSION: `"${props.marketingVersion}"`,
+ SWIFT_OPTIMIZATION_LEVEL: `"-Onone"`,
+ CODE_SIGN_ENTITLEMENTS: `"${props.targetName}/${props.targetName}.entitlements"`,
+ APPLICATION_EXTENSION_API_ONLY: '"YES"',
+ };
+ const buildConfigurationsList = [
+ {
+ name: 'Debug',
+ isa: 'XCBuildConfiguration',
+ buildSettings: {
+ ...commonBuildSettings,
+ },
+ },
+ {
+ name: 'Release',
+ isa: 'XCBuildConfiguration',
+ buildSettings: {
+ ...commonBuildSettings,
+ },
+ },
+ ];
+ const xCConfigurationList = xcodeProject.addXCConfigurationList(buildConfigurationsList, 'Release', `Build configuration list for PBXNativeTarget "${props.targetName}"`);
+ return xCConfigurationList;
+}
diff --git a/packages/expo-widgets/plugin/build/xcode/withTargetXcodeProject.d.ts b/packages/expo-widgets/plugin/build/xcode/withTargetXcodeProject.d.ts
new file mode 100644
index 00000000000000..b29fb5cfd8ea7b
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/withTargetXcodeProject.d.ts
@@ -0,0 +1,9 @@
+import { ConfigPlugin } from 'expo/config-plugins';
+type TargetXcodeProjectProps = {
+ targetName: string;
+ bundleIdentifier: string;
+ deploymentTarget: string;
+ getFileUris: () => string[];
+};
+declare const withTargetXcodeProject: ConfigPlugin;
+export default withTargetXcodeProject;
diff --git a/packages/expo-widgets/plugin/build/xcode/withTargetXcodeProject.js b/packages/expo-widgets/plugin/build/xcode/withTargetXcodeProject.js
new file mode 100644
index 00000000000000..24f56914ccb654
--- /dev/null
+++ b/packages/expo-widgets/plugin/build/xcode/withTargetXcodeProject.js
@@ -0,0 +1,48 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const config_plugins_1 = require("expo/config-plugins");
+const addBuildPhases_1 = require("./addBuildPhases");
+const addPbxGroup_1 = require("./addPbxGroup");
+const addProductFile_1 = require("./addProductFile");
+const addTargetDependency_1 = require("./addTargetDependency");
+const addToPbxNativeTargetSection_1 = require("./addToPbxNativeTargetSection");
+const addToPbxProjectSection_1 = require("./addToPbxProjectSection");
+const addXCConfigurationList_1 = require("./addXCConfigurationList");
+const withTargetXcodeProject = (config, { targetName, bundleIdentifier, deploymentTarget, getFileUris }) => (0, config_plugins_1.withXcodeProject)(config, (config) => {
+ const xcodeProject = config.modResults;
+ const targetUuid = xcodeProject.generateUuid();
+ const groupName = 'Embed Foundation Extensions';
+ const marketingVersion = config.version;
+ const xCConfigurationList = (0, addXCConfigurationList_1.addXCConfigurationList)(xcodeProject, {
+ targetName,
+ currentProjectVersion: config.ios.buildNumber || '1',
+ bundleIdentifier,
+ deploymentTarget,
+ marketingVersion,
+ });
+ const productFile = (0, addProductFile_1.addProductFile)(xcodeProject, {
+ targetName,
+ groupName,
+ });
+ const target = (0, addToPbxNativeTargetSection_1.addToPbxNativeTargetSection)(xcodeProject, {
+ targetName,
+ targetUuid,
+ productFile,
+ xCConfigurationList,
+ });
+ (0, addToPbxProjectSection_1.addToPbxProjectSection)(xcodeProject, target);
+ (0, addTargetDependency_1.addTargetDependency)(xcodeProject, target);
+ const swiftWidgetFiles = getFileUris().filter((file) => file.endsWith('.swift'));
+ (0, addBuildPhases_1.addBuildPhases)(xcodeProject, {
+ targetUuid,
+ groupName,
+ productFile,
+ widgetFiles: swiftWidgetFiles,
+ });
+ (0, addPbxGroup_1.addPbxGroup)(xcodeProject, {
+ targetName,
+ widgetFiles: getFileUris(),
+ });
+ return config;
+});
+exports.default = withTargetXcodeProject;
diff --git a/packages/expo-widgets/plugin/src/index.ts b/packages/expo-widgets/plugin/src/index.ts
new file mode 100644
index 00000000000000..ad96ce79f2acd4
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/index.ts
@@ -0,0 +1,70 @@
+import { ConfigPlugin, createRunOncePlugin, withPlugins } from 'expo/config-plugins';
+
+import { WidgetConfig } from './types/WidgetConfig.type';
+import withAppGroupEntitlements from './withAppGroupEntitlements';
+import withAppInfoPlist from './withAppInfoPlist';
+import withPodsLinking from './withPodsLinking';
+import withPushNotifications from './withPushNotifications';
+import withWidgetSourceFiles from './withWidgetSourceFiles';
+import withTargetXcodeProject from './xcode/withTargetXcodeProject';
+
+const pkg = require('expo-widgets/package.json');
+
+type ExpoWidgetsConfigPluginProps = {
+ // Widget target app bundle identifier. Defaults to `.ExpoWidgetsTarget`.
+ bundleIdentifier?: string;
+ // App group identifier used for communication between the main app and widgets. Defaults to `group.`.
+ groupIdentifier?: string;
+ // Enable push notifications for widgets. Defaults to false.
+ enablePushNotifications?: boolean;
+ // Enable frequent updates for widgets. Defaults to false.
+ frequentUpdates?: boolean;
+ widgets?: WidgetConfig[];
+};
+
+const withWidgets: ConfigPlugin = (config, props) => {
+ const deploymentTarget = '16.2';
+ const targetName = 'ExpoWidgetsTarget';
+
+ let bundleIdentifier = props.bundleIdentifier;
+ if (!bundleIdentifier) {
+ bundleIdentifier = `${config.ios?.bundleIdentifier}.${targetName}`;
+ console.log(`No bundleIdentifier provided, fallback to: ${bundleIdentifier}`);
+ }
+
+ let groupIdentifier = props.groupIdentifier;
+ if (!groupIdentifier) {
+ if (!config.ios?.bundleIdentifier) {
+ throw new Error(
+ 'iOS bundle identifier is required. Please set `ios.bundleIdentifier` in `app.json` or `app.config.js`'
+ );
+ }
+ groupIdentifier = `group.${config.ios.bundleIdentifier}`;
+ console.log(`No groupIdentifier provided, fallback to: ${groupIdentifier}`);
+ }
+
+ let widgets = props.widgets;
+ if (!widgets) {
+ widgets = [];
+ }
+
+ const enablePushNotifications = props.enablePushNotifications ?? false;
+ const frequentUpdates = props.frequentUpdates ?? false;
+
+ let sharedFiles: string[] = [];
+ const setFiles = (files: string[]) => {
+ sharedFiles = [...sharedFiles, ...files];
+ };
+ const getFileUris = () => sharedFiles;
+
+ return withPlugins(config, [
+ [withPodsLinking, { targetName }],
+ [withWidgetSourceFiles, { targetName, widgets, groupIdentifier, onFilesGenerated: setFiles }],
+ [withAppInfoPlist, { frequentUpdates, groupIdentifier }],
+ [withPushNotifications, { enablePushNotifications }],
+ [withAppGroupEntitlements, { groupIdentifier }],
+ [withTargetXcodeProject, { targetName, bundleIdentifier, deploymentTarget, getFileUris }],
+ ]);
+};
+
+export default createRunOncePlugin(withWidgets, pkg.name, pkg.version);
diff --git a/packages/expo-widgets/plugin/src/types/WidgetConfig.type.ts b/packages/expo-widgets/plugin/src/types/WidgetConfig.type.ts
new file mode 100644
index 00000000000000..6965d986ba2877
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/types/WidgetConfig.type.ts
@@ -0,0 +1,8 @@
+import { WidgetFamily } from './WidgetFamily.type';
+
+export type WidgetConfig = {
+ name: string;
+ supportedFamilies: WidgetFamily[];
+ displayName: string;
+ description: string;
+};
diff --git a/packages/expo-widgets/plugin/src/types/WidgetFamily.type.ts b/packages/expo-widgets/plugin/src/types/WidgetFamily.type.ts
new file mode 100644
index 00000000000000..664bba862558f9
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/types/WidgetFamily.type.ts
@@ -0,0 +1,9 @@
+export enum WidgetFamily {
+ systemSmall = 'systemSmall',
+ systemMedium = 'systemMedium',
+ systemLarge = 'systemLarge',
+ systemExtraLarge = 'systemExtraLarge',
+ accessoryCircular = 'accessoryCircular',
+ accessoryRectangular = 'accessoryRectangular',
+ accessoryInline = 'accessoryInline',
+}
diff --git a/packages/expo-widgets/plugin/src/withAppGroupEntitlements.ts b/packages/expo-widgets/plugin/src/withAppGroupEntitlements.ts
new file mode 100644
index 00000000000000..f80ed68a90a025
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/withAppGroupEntitlements.ts
@@ -0,0 +1,38 @@
+import { ConfigPlugin, InfoPlist, withEntitlementsPlist } from 'expo/config-plugins';
+
+type AppGroupEntitlementsProps = {
+ groupIdentifier: string;
+};
+
+const withAppGroupEntitlements: ConfigPlugin = (config, props) =>
+ withEntitlementsPlist(config, (config) => {
+ config.ios = {
+ ...config.ios,
+ entitlements: _addApplicationGroupsEntitlement(
+ config.ios?.entitlements ?? {},
+ props.groupIdentifier
+ ),
+ };
+
+ return config;
+ });
+
+export default withAppGroupEntitlements;
+
+function _addApplicationGroupsEntitlement(entitlements: InfoPlist, groupIdentifier?: string) {
+ if (!groupIdentifier) {
+ return entitlements;
+ }
+
+ const existingApplicationGroups =
+ (entitlements['com.apple.security.application-groups'] as string[]) ?? [];
+
+ if (!existingApplicationGroups.includes(groupIdentifier)) {
+ entitlements['com.apple.security.application-groups'] = [
+ groupIdentifier,
+ ...existingApplicationGroups,
+ ];
+ }
+
+ return entitlements;
+}
diff --git a/packages/expo-widgets/plugin/src/withAppInfoPlist.ts b/packages/expo-widgets/plugin/src/withAppInfoPlist.ts
new file mode 100644
index 00000000000000..c101de19da3786
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/withAppInfoPlist.ts
@@ -0,0 +1,20 @@
+import { ConfigPlugin, withInfoPlist } from 'expo/config-plugins';
+
+type withAppInfoPlistProps = {
+ frequentUpdates: boolean;
+ groupIdentifier: string;
+};
+
+const withAppInfoPlist: ConfigPlugin = (
+ config,
+ { frequentUpdates, groupIdentifier }
+) =>
+ withInfoPlist(config, (config) => {
+ const infoPlist = config.modResults;
+ infoPlist.NSSupportsLiveActivities = true;
+ infoPlist.NSSupportsLiveActivitiesFrequentUpdates = frequentUpdates;
+ infoPlist.ExpoWidgetsAppGroupIdentifier = groupIdentifier;
+ return config;
+ });
+
+export default withAppInfoPlist;
diff --git a/packages/expo-widgets/plugin/src/withPodsLinking.ts b/packages/expo-widgets/plugin/src/withPodsLinking.ts
new file mode 100644
index 00000000000000..d38c5b11a570a2
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/withPodsLinking.ts
@@ -0,0 +1,76 @@
+import { ConfigPlugin, withDangerousMod } from 'expo/config-plugins';
+import * as fs from 'fs';
+import * as path from 'path';
+
+interface PodsLinkingProps {
+ targetName: string;
+}
+
+const withPodsLinking: ConfigPlugin = (config, { targetName }) =>
+ withDangerousMod(config, [
+ 'ios',
+ async (config) => {
+ const podsFilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile');
+ let podfileContent = fs.readFileSync(podsFilePath, 'utf8');
+ if (podfileContent.includes(`target "${targetName}" do`)) {
+ return config;
+ }
+ podfileContent += podfileExpoWidgetsLinking(targetName);
+ // expo_widgets_post_install hook
+ if (!podfileContent.includes('post_install do |installer|')) {
+ podfileContent += `
+post_install do |installer|
+ expo_widgets_post_install(installer)
+end
+`;
+ } else {
+ podfileContent = podfileContent.replace(
+ /post_install do \|installer\|([\s\S]*?)end\s*$/m,
+ (match, p1) => {
+ return `post_install do |installer|
+ expo_widgets_post_install(installer)
+${p1}end
+`;
+ }
+ );
+ }
+ fs.writeFileSync(podsFilePath, podfileContent, 'utf8');
+ return config;
+ },
+ ]);
+
+export default withPodsLinking;
+
+const podfileExpoWidgetsLinking = (targetName: string) => `
+require File.join(File.dirname(\`node --print "require.resolve('expo-widgets/package.json')"\`), "scripts/autolinking")
+target "${targetName}" do
+ use_expo_modules_widgets!
+
+ if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
+ config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
+ else
+ config_command = [
+ 'node',
+ '--no-warnings',
+ '--eval',
+ "require(require.resolve('expo-modules-autolinking', { paths: [require.resolve('expo/package.json')] }))(process.argv.slice(1))",
+ 'react-native-config',
+ '--json',
+ '--platform',
+ 'ios'
+ ]
+ end
+
+ config = use_expo_native_module!(config_command)
+
+ use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
+ use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
+
+ use_react_native!(
+ :path => config[:reactNativePath],
+ :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
+ :app_path => "#{Pod::Config.instance.installation_root}/..",
+ :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
+ )
+end
+`;
diff --git a/packages/expo-widgets/plugin/src/withPushNotifications.ts b/packages/expo-widgets/plugin/src/withPushNotifications.ts
new file mode 100644
index 00000000000000..30de9fbf1a3a88
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/withPushNotifications.ts
@@ -0,0 +1,19 @@
+import { ConfigPlugin, withEntitlementsPlist, withInfoPlist } from 'expo/config-plugins';
+
+type PushNotificationProps = {
+ enablePushNotifications: boolean;
+};
+
+const withPushNotifications: ConfigPlugin = (config, props) =>
+ withInfoPlist(
+ withEntitlementsPlist(config, (mod) => {
+ mod.modResults['aps-environment'] = 'development';
+ return mod;
+ }),
+ (mod) => {
+ mod.modResults['ExpoLiveActivity_EnablePushNotifications'] = props.enablePushNotifications;
+ return mod;
+ }
+ );
+
+export default withPushNotifications;
diff --git a/packages/expo-widgets/plugin/src/withWidgetSourceFiles.ts b/packages/expo-widgets/plugin/src/withWidgetSourceFiles.ts
new file mode 100644
index 00000000000000..3a2e80d2c09890
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/withWidgetSourceFiles.ts
@@ -0,0 +1,124 @@
+import plist from '@expo/plist';
+import { ConfigPlugin, withDangerousMod } from 'expo/config-plugins';
+import * as fs from 'fs';
+import * as path from 'path';
+
+import { WidgetConfig } from './types/WidgetConfig.type';
+
+type WidgetSourceFilesProps = {
+ targetName: string;
+ groupIdentifier: string;
+ widgets: WidgetConfig[];
+ onFilesGenerated: (files: string[]) => void;
+};
+
+const withWidgetSourceFiles: ConfigPlugin = (
+ config,
+ { widgets, targetName, onFilesGenerated, groupIdentifier }
+) =>
+ withDangerousMod(config, [
+ 'ios',
+ async (config) => {
+ const projectRoot = config.modRequest.platformProjectRoot;
+ const targetDirectory = path.join(projectRoot, targetName);
+ if (fs.existsSync(targetDirectory)) {
+ fs.rmSync(targetDirectory, { recursive: true, force: true });
+ }
+ if (!fs.existsSync(targetDirectory)) {
+ fs.mkdirSync(targetDirectory, { recursive: true });
+ }
+ const entitlementsPath = path.join(targetDirectory, `${targetName}.entitlements`);
+ const entitlementsContent = {
+ 'com.apple.security.application-groups': [groupIdentifier],
+ };
+ fs.writeFileSync(entitlementsPath, plist.build(entitlementsContent));
+
+ const infoPlistPath = createInfoPlist(groupIdentifier, targetDirectory);
+ const indexSwiftPath = createIndexSwift(widgets, targetDirectory);
+ const widgetSwiftPaths = widgets.map((widget) => createWidgetSwift(widget, targetDirectory));
+
+ onFilesGenerated([entitlementsPath, infoPlistPath, indexSwiftPath, ...widgetSwiftPaths]);
+
+ return config;
+ },
+ ]);
+
+const createIndexSwift = (widgets: WidgetConfig[], targetPath: string): string => {
+ const indexFilePath = path.join(targetPath, `index.swift`);
+ const numberOfWidgets = widgets.length;
+ const numberOfBundles = Math.ceil(numberOfWidgets / 4);
+ let output = `import WidgetKit
+import SwiftUI
+import ExpoWidgets
+`;
+
+ for (let i = 0; i < numberOfBundles; i++) {
+ const start = i * 4;
+ const end = Math.min(start + 4, numberOfWidgets);
+ const widgetChunk = widgets.slice(start, end);
+ const isLastChunk = i === numberOfBundles - 1;
+ output += addIndexSwiftChunk(widgetChunk, i, isLastChunk);
+ }
+
+ fs.writeFileSync(indexFilePath, output);
+ return indexFilePath;
+};
+
+const createWidgetSwift = (widget: WidgetConfig, targetPath: string): string => {
+ const widgetFilePath = path.join(targetPath, `${widget.name}.swift`);
+ fs.writeFileSync(widgetFilePath, widgetSwift(widget));
+ return widgetFilePath;
+};
+
+const createInfoPlist = (groupIdentifier: string, targetPath: string): string => {
+ const infoPlistPath = `${targetPath}/Info.plist`;
+ fs.writeFileSync(infoPlistPath, infoPlist(groupIdentifier));
+ return infoPlistPath;
+};
+
+const addIndexSwiftChunk = (
+ widgets: WidgetConfig[],
+ index: number,
+ isLastChunk: boolean
+): string => `
+${index === 0 ? '@main' : ''}
+struct ExportWidgets${index}: WidgetBundle {
+ var body: some Widget {
+ ${widgets.map((widget) => `${widget.name}()`).join('\n\t\t')}
+ ${!isLastChunk ? `ExportWidgets${index + 1}().body` : `WidgetLiveActivity()`}
+ }
+}`;
+
+const widgetSwift = (widget: WidgetConfig): string => `import WidgetKit
+import SwiftUI
+import ExpoWidgets
+
+struct ${widget.name}: Widget {
+ let name: String = "${widget.name}"
+
+ var body: some WidgetConfiguration {
+ StaticConfiguration(kind: name, provider: WidgetsTimelineProvider(name: name)) { entry in
+ WidgetsEntryView(entry: entry)
+ }
+ .configurationDisplayName("${widget.displayName}")
+ .description("${widget.description}")
+ .supportedFamilies([.${widget.supportedFamilies.join(', .')}])
+ }
+}`;
+
+const infoPlist = (groupIdentifier: string) => `
+
+
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.widgetkit-extension
+
+ ExpoWidgetsAppGroupIdentifier
+ ${groupIdentifier}
+
+
+`;
+
+export default withWidgetSourceFiles;
diff --git a/packages/expo-widgets/plugin/src/xcode/addBuildPhases.ts b/packages/expo-widgets/plugin/src/xcode/addBuildPhases.ts
new file mode 100644
index 00000000000000..bed029f692282b
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/xcode/addBuildPhases.ts
@@ -0,0 +1,63 @@
+import { XcodeProject } from 'expo/config-plugins';
+import * as util from 'util';
+
+export function addBuildPhases(
+ xcodeProject: XcodeProject,
+ {
+ targetUuid,
+ groupName,
+ productFile,
+ widgetFiles,
+ }: {
+ targetUuid: string;
+ groupName: string;
+ productFile: {
+ uuid: string;
+ target: string;
+ basename: string;
+ group: string;
+ };
+ widgetFiles: string[];
+ }
+) {
+ const buildPath = `""`;
+ const folderType = 'app_extension';
+
+ // Sources build phase
+ xcodeProject.addBuildPhase(
+ [...widgetFiles],
+ 'PBXSourcesBuildPhase',
+ groupName,
+ targetUuid,
+ folderType,
+ buildPath
+ );
+
+ // Copy files build phase
+ xcodeProject.addBuildPhase(
+ [],
+ 'PBXCopyFilesBuildPhase',
+ groupName,
+ xcodeProject.getFirstTarget().uuid,
+ folderType,
+ buildPath
+ );
+
+ xcodeProject
+ .buildPhaseObject('PBXCopyFilesBuildPhase', groupName, productFile.target)
+ .files.push({
+ value: productFile.uuid,
+ comment: util.format('%s in %s', productFile.basename, productFile.group), // longComment(file);
+ });
+ xcodeProject.addToPbxBuildFileSection(productFile);
+
+ // Frameworks build phase
+ xcodeProject.addBuildPhase(
+ [],
+ 'PBXFrameworksBuildPhase',
+ groupName,
+ targetUuid,
+ folderType,
+ buildPath
+ );
+}
diff --git a/packages/expo-widgets/plugin/src/xcode/addPbxGroup.ts b/packages/expo-widgets/plugin/src/xcode/addPbxGroup.ts
new file mode 100644
index 00000000000000..9725db4aa4c4cf
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/xcode/addPbxGroup.ts
@@ -0,0 +1,29 @@
+import { XcodeProject } from 'expo/config-plugins';
+
+export function addPbxGroup(
+ xcodeProject: XcodeProject,
+ {
+ targetName,
+ widgetFiles,
+ }: {
+ targetName: string;
+ widgetFiles: string[];
+ }
+) {
+ // Add PBX group
+ const { uuid: pbxGroupUuid } = xcodeProject.addPbxGroup(
+ [...widgetFiles, `${targetName}.entitlements`],
+ targetName,
+ targetName
+ );
+
+ // Add PBXGroup to top level group
+ const groups = xcodeProject.hash.project.objects['PBXGroup'];
+ if (pbxGroupUuid) {
+ Object.keys(groups).forEach(function (key) {
+ if (groups[key].name === undefined && groups[key].path === undefined) {
+ xcodeProject.addToPbxGroup(pbxGroupUuid, key);
+ }
+ });
+ }
+}
diff --git a/packages/expo-widgets/plugin/src/xcode/addProductFile.ts b/packages/expo-widgets/plugin/src/xcode/addProductFile.ts
new file mode 100644
index 00000000000000..2bf1d49208a747
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/xcode/addProductFile.ts
@@ -0,0 +1,25 @@
+import { XcodeProject } from 'expo/config-plugins';
+
+type AddProductFileProps = {
+ targetName: string;
+ groupName: string;
+};
+
+export function addProductFile(
+ xcodeProject: XcodeProject,
+ { targetName, groupName }: AddProductFileProps
+) {
+ const options = {
+ basename: `${targetName}.appex`,
+ group: groupName,
+ explicitFileType: 'wrapper.app-extension',
+ settings: {
+ ATTRIBUTES: ['RemoveHeadersOnCopy'],
+ },
+ includeInIndex: 0,
+ path: `${targetName}.appex`,
+ sourceTree: 'BUILT_PRODUCTS_DIR',
+ };
+
+ return xcodeProject.addProductFile(targetName, options);
+}
diff --git a/packages/expo-widgets/plugin/src/xcode/addTargetDependency.ts b/packages/expo-widgets/plugin/src/xcode/addTargetDependency.ts
new file mode 100644
index 00000000000000..b63a05ac402c8f
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/xcode/addTargetDependency.ts
@@ -0,0 +1,12 @@
+import { XcodeProject } from 'expo/config-plugins';
+
+export function addTargetDependency(xcodeProject: XcodeProject, target: { uuid: string }) {
+ if (!xcodeProject.hash.project.objects['PBXTargetDependency']) {
+ xcodeProject.hash.project.objects['PBXTargetDependency'] = {};
+ }
+ if (!xcodeProject.hash.project.objects['PBXContainerItemProxy']) {
+ xcodeProject.hash.project.objects['PBXContainerItemProxy'] = {};
+ }
+
+ xcodeProject.addTargetDependency(xcodeProject.getFirstTarget().uuid, [target.uuid]);
+}
diff --git a/packages/expo-widgets/plugin/src/xcode/addToPbxNativeTargetSection.ts b/packages/expo-widgets/plugin/src/xcode/addToPbxNativeTargetSection.ts
new file mode 100644
index 00000000000000..03ced1aa6b06b9
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/xcode/addToPbxNativeTargetSection.ts
@@ -0,0 +1,34 @@
+import { XcodeProject } from 'expo/config-plugins';
+
+export function addToPbxNativeTargetSection(
+ xcodeProject: XcodeProject,
+ {
+ targetName,
+ targetUuid,
+ productFile,
+ xCConfigurationList,
+ }: {
+ targetName: string;
+ targetUuid: string;
+ productFile: { fileRef: string };
+ xCConfigurationList: { uuid: string };
+ }
+) {
+ const target = {
+ uuid: targetUuid,
+ pbxNativeTarget: {
+ isa: 'PBXNativeTarget',
+ name: targetName,
+ productName: targetName,
+ productReference: productFile.fileRef,
+ productType: `"com.apple.product-type.app-extension"`,
+ buildConfigurationList: xCConfigurationList.uuid,
+ buildPhases: [],
+ buildRules: [],
+ dependencies: [],
+ },
+ };
+
+ xcodeProject.addToPbxNativeTargetSection(target);
+ return target;
+}
diff --git a/packages/expo-widgets/plugin/src/xcode/addToPbxProjectSection.ts b/packages/expo-widgets/plugin/src/xcode/addToPbxProjectSection.ts
new file mode 100644
index 00000000000000..c0ae9b3f8c7528
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/xcode/addToPbxProjectSection.ts
@@ -0,0 +1,20 @@
+import { XcodeProject } from 'expo/config-plugins';
+
+export function addToPbxProjectSection(xcodeProject: XcodeProject, target: { uuid: string }) {
+ xcodeProject.addToPbxProjectSection(target);
+
+ // Add target attributes to project section
+ if (
+ !xcodeProject.pbxProjectSection()[xcodeProject.getFirstProject().uuid].attributes
+ .TargetAttributes
+ ) {
+ xcodeProject.pbxProjectSection()[
+ xcodeProject.getFirstProject().uuid
+ ].attributes.TargetAttributes = {};
+ }
+ xcodeProject.pbxProjectSection()[xcodeProject.getFirstProject().uuid].attributes.TargetAttributes[
+ target.uuid
+ ] = {
+ LastSwiftMigration: 1250,
+ };
+}
diff --git a/packages/expo-widgets/plugin/src/xcode/addXCConfigurationList.ts b/packages/expo-widgets/plugin/src/xcode/addXCConfigurationList.ts
new file mode 100644
index 00000000000000..c79e169c512bc0
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/xcode/addXCConfigurationList.ts
@@ -0,0 +1,56 @@
+import { XcodeProject } from 'expo/config-plugins';
+
+interface AddXCConfigurationListProps {
+ targetName: string;
+ currentProjectVersion: string;
+ bundleIdentifier: string;
+ deploymentTarget: string;
+ marketingVersion?: string;
+}
+
+export function addXCConfigurationList(
+ xcodeProject: XcodeProject,
+ props: AddXCConfigurationListProps
+) {
+ const commonBuildSettings: any = {
+ PRODUCT_NAME: `"$(TARGET_NAME)"`,
+ SWIFT_VERSION: '5.0',
+ TARGETED_DEVICE_FAMILY: `"1,2"`,
+ INFOPLIST_FILE: `${props.targetName}/Info.plist`,
+ CURRENT_PROJECT_VERSION: `"${props.currentProjectVersion}"`,
+ IPHONEOS_DEPLOYMENT_TARGET: `"${props.deploymentTarget}"`,
+ PRODUCT_BUNDLE_IDENTIFIER: `"${props.bundleIdentifier}"`,
+ GENERATE_INFOPLIST_FILE: `"YES"`,
+ INFOPLIST_KEY_CFBundleDisplayName: props.targetName,
+ INFOPLIST_KEY_NSHumanReadableCopyright: `""`,
+ MARKETING_VERSION: `"${props.marketingVersion}"`,
+ SWIFT_OPTIMIZATION_LEVEL: `"-Onone"`,
+ CODE_SIGN_ENTITLEMENTS: `"${props.targetName}/${props.targetName}.entitlements"`,
+ APPLICATION_EXTENSION_API_ONLY: '"YES"',
+ };
+
+ const buildConfigurationsList = [
+ {
+ name: 'Debug',
+ isa: 'XCBuildConfiguration',
+ buildSettings: {
+ ...commonBuildSettings,
+ },
+ },
+ {
+ name: 'Release',
+ isa: 'XCBuildConfiguration',
+ buildSettings: {
+ ...commonBuildSettings,
+ },
+ },
+ ];
+
+ const xCConfigurationList = xcodeProject.addXCConfigurationList(
+ buildConfigurationsList,
+ 'Release',
+ `Build configuration list for PBXNativeTarget "${props.targetName}"`
+ );
+
+ return xCConfigurationList;
+}
diff --git a/packages/expo-widgets/plugin/src/xcode/withTargetXcodeProject.ts b/packages/expo-widgets/plugin/src/xcode/withTargetXcodeProject.ts
new file mode 100644
index 00000000000000..6ead742e95a344
--- /dev/null
+++ b/packages/expo-widgets/plugin/src/xcode/withTargetXcodeProject.ts
@@ -0,0 +1,69 @@
+import { ConfigPlugin, withXcodeProject } from 'expo/config-plugins';
+
+import { addBuildPhases } from './addBuildPhases';
+import { addPbxGroup } from './addPbxGroup';
+import { addProductFile } from './addProductFile';
+import { addTargetDependency } from './addTargetDependency';
+import { addToPbxNativeTargetSection } from './addToPbxNativeTargetSection';
+import { addToPbxProjectSection } from './addToPbxProjectSection';
+import { addXCConfigurationList } from './addXCConfigurationList';
+
+type TargetXcodeProjectProps = {
+ targetName: string;
+ bundleIdentifier: string;
+ deploymentTarget: string;
+ getFileUris: () => string[];
+};
+
+const withTargetXcodeProject: ConfigPlugin = (
+ config,
+ { targetName, bundleIdentifier, deploymentTarget, getFileUris }
+) =>
+ withXcodeProject(config, (config) => {
+ const xcodeProject = config.modResults;
+ const targetUuid = xcodeProject.generateUuid();
+ const groupName = 'Embed Foundation Extensions';
+ const marketingVersion = config.version;
+
+ const xCConfigurationList = addXCConfigurationList(xcodeProject, {
+ targetName,
+ currentProjectVersion: config.ios!.buildNumber || '1',
+ bundleIdentifier,
+ deploymentTarget,
+ marketingVersion,
+ });
+
+ const productFile = addProductFile(xcodeProject, {
+ targetName,
+ groupName,
+ });
+
+ const target = addToPbxNativeTargetSection(xcodeProject, {
+ targetName,
+ targetUuid,
+ productFile,
+ xCConfigurationList,
+ });
+
+ addToPbxProjectSection(xcodeProject, target);
+
+ addTargetDependency(xcodeProject, target);
+
+ const swiftWidgetFiles = getFileUris().filter((file) => file.endsWith('.swift'));
+
+ addBuildPhases(xcodeProject, {
+ targetUuid,
+ groupName,
+ productFile,
+ widgetFiles: swiftWidgetFiles,
+ });
+
+ addPbxGroup(xcodeProject, {
+ targetName,
+ widgetFiles: getFileUris(),
+ });
+
+ return config;
+ });
+
+export default withTargetXcodeProject;
diff --git a/packages/expo-widgets/plugin/tsconfig.json b/packages/expo-widgets/plugin/tsconfig.json
new file mode 100644
index 00000000000000..354bddb4336eb1
--- /dev/null
+++ b/packages/expo-widgets/plugin/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "expo-module-scripts/tsconfig.plugin",
+ "compilerOptions": {
+ "outDir": "build",
+ "rootDir": "src"
+ },
+ "include": ["./src"],
+ "exclude": ["**/__mocks__/*", "**/__tests__/*"]
+}
diff --git a/packages/expo-widgets/scripts/autolinking.rb b/packages/expo-widgets/scripts/autolinking.rb
new file mode 100644
index 00000000000000..651db0ef6ac93a
--- /dev/null
+++ b/packages/expo-widgets/scripts/autolinking.rb
@@ -0,0 +1,34 @@
+require 'json'
+require 'pathname'
+require 'colored2' # dependency of CocoaPods
+
+require File.join(File.dirname(`node --print "require.resolve('expo-modules-autolinking/package.json', { paths: ['#{__dir__}'] })"`), "scripts/ios/autolinking_manager")
+require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
+
+def use_expo_modules_widgets!(options = {})
+ output = Expo::AutolinkingManager.new(self, @current_target_definition, options).resolve
+
+ all_packages = output["modules"].map { |mod| mod["packageName"] }
+ used_packages = ["expo", "expo-widgets", "@expo/ui"]
+ packages_to_exclude = all_packages - used_packages
+
+ options[:exclude] = packages_to_exclude
+ use_expo_modules!(options)
+end
+
+def use_expo_native_module!(config_command = $default_command)
+ config = list_native_modules!(config_command)
+ config[:ios_packages].select! { |package| package[:name] == "expo" }
+
+ return link_native_modules!(config)
+end
+
+def expo_widgets_post_install(installer)
+ installer.pods_project.targets.each do |target|
+ if target.name == 'ExpoModulesCore'
+ target.build_configurations.each do |config|
+ config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'No'
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/packages/expo/bundledNativeModules.json b/packages/expo/bundledNativeModules.json
index f59282d090761b..407ae39eca9e51 100644
--- a/packages/expo/bundledNativeModules.json
+++ b/packages/expo/bundledNativeModules.json
@@ -103,7 +103,7 @@
"react-native-pager-view": "8.0.0",
"react-native-worklets": "0.7.1",
"react-native-reanimated": "~4.2.1",
- "react-native-screens": "~4.19.0",
+ "react-native-screens": "~4.20.0",
"react-native-safe-area-context": "~5.6.2",
"react-native-svg": "15.15.1",
"react-native-view-shot": "4.0.3",
diff --git a/packages/expo/ios/AppDelegates/ExpoAppDelegate.swift b/packages/expo/ios/AppDelegates/ExpoAppDelegate.swift
index 2c4f583f2410ab..09766c71fd8768 100644
--- a/packages/expo/ios/AppDelegates/ExpoAppDelegate.swift
+++ b/packages/expo/ios/AppDelegates/ExpoAppDelegate.swift
@@ -1,7 +1,5 @@
-import Dispatch
import Foundation
import ExpoModulesCore
-import ReactAppDependencyProvider
/**
Allows classes extending `ExpoAppDelegateSubscriber` to hook into project's app delegate
@@ -109,7 +107,7 @@ open class ExpoAppDelegate: UIResponder, UIApplicationDelegate {
#endif
// MARK: - Responding to Environment Changes
-
+
#if os(iOS) || os(tvOS)
open func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
diff --git a/templates/expo-template-default/package.json b/templates/expo-template-default/package.json
index b1d79fa6cd073e..27f1a49f4426e7 100644
--- a/templates/expo-template-default/package.json
+++ b/templates/expo-template-default/package.json
@@ -34,7 +34,7 @@
"react-native-worklets": "0.7.1",
"react-native-reanimated": "~4.2.1",
"react-native-safe-area-context": "~5.6.2",
- "react-native-screens": "~4.19.0",
+ "react-native-screens": "~4.20.0",
"react-native-web": "~0.21.0"
},
"devDependencies": {
diff --git a/templates/expo-template-tabs/package.json b/templates/expo-template-tabs/package.json
index c32439305fa048..aa5d2a96198663 100644
--- a/templates/expo-template-tabs/package.json
+++ b/templates/expo-template-tabs/package.json
@@ -27,7 +27,7 @@
"react-native-worklets": "0.7.1",
"react-native-reanimated": "~4.2.1",
"react-native-safe-area-context": "~5.6.2",
- "react-native-screens": "~4.19.0",
+ "react-native-screens": "~4.20.0",
"react-native-web": "~0.21.0"
},
"devDependencies": {
diff --git a/yarn.lock b/yarn.lock
index 2ac5737389d331..85af716c8f64a1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4343,6 +4343,11 @@
resolved "https://registry.yarnpkg.com/@types/dedent/-/dedent-0.7.0.tgz#155f339ca404e6dd90b9ce46a3f78fd69ca9b050"
integrity sha512-EGlKlgMhnLt/cM4DbUSafFdrkeJoC9Mvnj0PUCU7tFmTjMjNRT957kXCx0wYm3JuEq4o4ZsS5vG+NlkM2DMd2A==
+"@types/diff@^5.2.0":
+ version "5.2.3"
+ resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.2.3.tgz#dcdcfa40df9f011f9465180e0196dfbd921971d9"
+ integrity sha512-K0Oqlrq3kQMaO2RhfrNQX5trmt+XLyom88zS0u84nnIcLvFnRUMRRHmrGny5GSM+kNO9IZLARsdQHDzkhAgmrQ==
+
"@types/ejs@^3.1.5":
version "3.1.5"
resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.5.tgz#49d738257cc73bafe45c13cb8ff240683b4d5117"
@@ -7293,6 +7298,11 @@ diff@^4.0.1:
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+diff@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531"
+ integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==
+
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@@ -13481,10 +13491,10 @@ react-native-screens@4.11.1-nightly-20250611-8b82e081e:
react-native-is-edge-to-edge "^1.1.7"
warn-once "^0.1.0"
-react-native-screens@4.19.0:
- version "4.19.0"
- resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.19.0.tgz#d00f2ec070d7426204eaf34ef6c64396a6871e1d"
- integrity sha512-qSDAO3AL5bti0Ri7KZRSVmWlhDr8MV86N5GruiKVQfEL7Zx2nUi3Dl62lqHUAD/LnDvOPuDDsMHCfIpYSv3hPQ==
+react-native-screens@4.20.0:
+ version "4.20.0"
+ resolved "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.20.0.tgz#16fc363a23f51495a15166982aadae58b0115947"
+ integrity sha512-wg3ILSd8yHM2YMsWqDjr1+Rxj1qn9CrzZ8qAqDXYd+jf6p3GIMwi+NugFUbRBRZMXs3MNEXCS1vAkvc2ZwpaAA==
dependencies:
react-freeze "^1.0.0"
warn-once "^0.1.0"