diff --git a/packages/@expo/cli/CHANGELOG.md b/packages/@expo/cli/CHANGELOG.md index e9c89433e73abe..d18a6bfe6d30de 100644 --- a/packages/@expo/cli/CHANGELOG.md +++ b/packages/@expo/cli/CHANGELOG.md @@ -6,13 +6,19 @@ ### 🎉 New features +- Prefix web client logs with platform name in Metro terminal output. ([#45516](https://github.com/expo/expo/pull/45516) by [@EvanBacon](https://github.com/EvanBacon)) + ### 🐛 Bug fixes +- Fix Metro progress bars appearing as permanent output due to cursor corruption from stderr writes and stale status snapshots. ([#45523](https://github.com/expo/expo/pull/45523) by [@EvanBacon](https://github.com/EvanBacon)) - Prevent Metro loading indicator from showing broken states in headless runs. ([#45513](https://github.com/expo/expo/pull/45513) by [@EvanBacon](https://github.com/EvanBacon)) - Fix `--port 0` exiting silently in `expo start` when the port is busy. ([#45513](https://github.com/expo/expo/pull/45513) by [@EvanBacon](https://github.com/EvanBacon)) - +- Apply printf-style format substitution for web client logs forwarded from the browser. ([#45516](https://github.com/expo/expo/pull/45516) by [@EvanBacon](https://github.com/EvanBacon)) ### 💡 Others +- Replace deprecated `url.parse()` with WHATWG `URL` API in Metro dev server. ([#45524](https://github.com/expo/expo/pull/45524) by [@EvanBacon](https://github.com/EvanBacon)) +- Remove pinned dependencies ([#45520](https://github.com/expo/expo/pull/45520) by [@kitten](https://githun.com/kitten)) + ## 56.0.6 — 2026-05-07 ### 🐛 Bug fixes diff --git a/packages/@expo/cli/package.json b/packages/@expo/cli/package.json index 614b391f195c89..2642ed9625845b 100644 --- a/packages/@expo/cli/package.json +++ b/packages/@expo/cli/package.json @@ -55,12 +55,12 @@ "@expo/devcert": "^1.2.1", "@expo/env": "workspace:~2.2.0", "@expo/image-utils": "workspace:^0.9.0", - "@expo/inline-modules": "workspace:0.0.4", + "@expo/inline-modules": "workspace:^0.0.4", "@expo/json-file": "workspace:^10.1.0", - "@expo/log-box": "workspace:56.0.5", + "@expo/log-box": "workspace:^56.0.5", "@expo/metro": "~56.0.0", "@expo/metro-config": "workspace:~56.0.4", - "@expo/metro-file-map": "workspace:56.0.0-2", + "@expo/metro-file-map": "workspace:^56.0.0-2", "@expo/osascript": "workspace:^2.5.0", "@expo/package-manager": "workspace:^1.11.0", "@expo/plist": "workspace:^0.6.0", diff --git a/packages/@expo/cli/src/start/server/metro/MetroTerminalReporter.ts b/packages/@expo/cli/src/start/server/metro/MetroTerminalReporter.ts index 58dfdfd32c8b0a..3966a0f5d6f3df 100644 --- a/packages/@expo/cli/src/start/server/metro/MetroTerminalReporter.ts +++ b/packages/@expo/cli/src/start/server/metro/MetroTerminalReporter.ts @@ -1,7 +1,7 @@ import type { Terminal } from '@expo/metro/metro-core'; import chalk from 'chalk'; import path from 'path'; -import { stripVTControlCharacters } from 'util'; +import { format as utilFormat, stripVTControlCharacters } from 'util'; import { logWarning, TerminalReporter } from './TerminalReporter'; import type { @@ -335,8 +335,17 @@ export class MetroTerminalReporter extends TerminalReporter { } } - #onClientLog(evt: { type: 'client_log'; level?: ClientLogLevel; data: unknown[] }) { - const { level = 'log', data } = evt; + #onClientLog(evt: { + type: 'client_log'; + level?: ClientLogLevel; + data: unknown[]; + mode?: string; + }) { + const { level = 'log' } = evt; + // Apply printf-style format substitution (e.g. %s, %d) that browsers handle + // natively in console methods but Node/Metro terminal logging does not. + const data = applyConsoleFormatting(evt.data); + const platformTag = getPlatformTagForClientLog(evt.mode); if (level === 'warn' || (level as string) === 'error') { let hasStack = false; const parsed = data.map((msg) => { @@ -399,7 +408,7 @@ export class MetroTerminalReporter extends TerminalReporter { : symbolicated; event('client_log', { level, data: symbolicated }); - logLikeMetro(this.terminal.log.bind(this.terminal), level, null, ...filtered); + logLikeMetro(this.terminal.log.bind(this.terminal), level, platformTag, ...filtered); })(); return; } @@ -407,7 +416,7 @@ export class MetroTerminalReporter extends TerminalReporter { event('client_log', { level, data }); // Overwrite the Metro terminal logging so we can improve the warnings, symbolicate stacks, and inject extra info. - logLikeMetro(this.terminal.log.bind(this.terminal), level, null, ...data); + logLikeMetro(this.terminal.log.bind(this.terminal), level, platformTag, ...data); } #captureLog(evt: TerminalReportableEvent) { @@ -552,11 +561,51 @@ function isAppRegistryStartupMessage(body: any[]): boolean { ); } +/** Apply printf-style format substitutions (%s, %d, %i, %f, %o, %O) that browsers handle natively */ +function applyConsoleFormatting(data: unknown[]): unknown[] { + if (data.length <= 1 || typeof data[0] !== 'string' || !/%[sdifoO%]/.test(data[0])) { + return data; + } + return [utilFormat(...(data as [string, ...unknown[]]))]; +} + +/** @returns formatted platform name for a client log event, or null if no prefix should be shown */ +function getPlatformTagForClientLog(mode?: string): string | null { + switch (mode) { + case 'ios': + return 'iOS'; + case 'android': + return 'Android'; + case 'web': + return 'Web'; + case 'dom': + return 'DOM'; + default: + return null; + } +} + /** @returns platform specific tag for a `BundleDetails` object */ function getPlatformTagForBuildDetails(bundleDetails?: BundleDetails | null): string { const platform = bundleDetails?.platform ?? null; if (platform) { - const formatted = { ios: 'iOS', android: 'Android', web: 'Web' }[platform] || platform; + let formatted: string; + switch (platform) { + case 'ios': + formatted = 'iOS'; + break; + case 'android': + formatted = 'Android'; + break; + case 'web': + formatted = 'Web'; + break; + case 'dom': + formatted = 'DOM'; + break; + default: + formatted = platform; + } return `${chalk.bold(formatted)} `; } diff --git a/packages/@expo/cli/src/start/server/metro/TerminalReporter.ts b/packages/@expo/cli/src/start/server/metro/TerminalReporter.ts index 7702944c58600d..fc477b8955e9c9 100644 --- a/packages/@expo/cli/src/start/server/metro/TerminalReporter.ts +++ b/packages/@expo/cli/src/start/server/metro/TerminalReporter.ts @@ -59,6 +59,29 @@ export class TerminalReporter extends XTerminalReporter implements TerminalRepor /** Keep track of bundle processes that should not be logged. */ _hiddenBundleEvents: Set = new Set(); + /** + * Override Metro's update() to clear the status before _log() runs. + * + * Metro's Terminal.#update() is async and snapshots #nextStatusStr at the start. + * When _log() calls terminal.log(), Terminal starts #update() which captures + * whatever status is currently set — often a stale progress bar. This progress bar + * gets written as permanent output between log lines because the next #update() + * cycle (which would clear it) runs 33ms later. + * + * By clearing the status to empty before _log(), Terminal's #update() captures + * an empty status and doesn't write any progress bars alongside log lines. + * The correct status is then restored by terminal.status() at the end. + */ + update(event: TerminalReportableEvent): void { + if ( + event.type !== 'bundle_transform_progressed' && + event.type !== ('bundle_transform_progressed_throttled' as string) + ) { + this.terminal.status(''); + } + super.update(event); + } + _log(event: TerminalReportableEvent): void { switch (event.type) { case 'transform_cache_reset': diff --git a/packages/@expo/cli/src/start/server/metro/__tests__/MetroTerminalReporter-test.ts b/packages/@expo/cli/src/start/server/metro/__tests__/MetroTerminalReporter-test.ts index c4826811b8ca34..14536d62343180 100644 --- a/packages/@expo/cli/src/start/server/metro/__tests__/MetroTerminalReporter-test.ts +++ b/packages/@expo/cli/src/start/server/metro/__tests__/MetroTerminalReporter-test.ts @@ -27,6 +27,11 @@ const LIGHT_BLOCK_CHAR = '\u2591'; jest.useFakeTimers(); +const mockIsInteractive = jest.fn(() => false); +jest.mock('../../../../utils/interactive', () => ({ + isInteractive: () => mockIsInteractive(), +})); + jest.mock('../../serverLogLikeMetro', () => { const original = jest.requireActual('../../serverLogLikeMetro'); return { @@ -199,6 +204,92 @@ describe('symbolicate React stacks', () => { }); }); +describe('client log platform prefix and format substitution', () => { + const terminal = { + log: jest.fn(), + persistStatus: jest.fn(), + status: jest.fn(), + flush: jest.fn(), + _update: jest.fn(), + } satisfies Partial; + + const reporter = new MetroTerminalReporter('/', terminal as any); + + beforeEach(() => { + terminal.log.mockReset(); + }); + + it(`prefixes web client logs with "Web"`, () => { + reporter._log({ + type: 'client_log', + level: 'log', + data: ['hello world'], + mode: 'web', + } as any); + + expect(terminal.log).toHaveBeenCalledTimes(1); + const output = stripVTControlCharacters(terminal.log.mock.calls[0].join('')); + expect(output).toMatch(/^Web /); + expect(output).toContain('LOG'); + expect(output).toContain('hello world'); + }); + + it(`does not prefix native client logs (NOBRIDGE)`, () => { + reporter._log({ + type: 'client_log', + level: 'log', + data: ['hello world'], + mode: 'NOBRIDGE', + } as any); + + expect(terminal.log).toHaveBeenCalledTimes(1); + const output = stripVTControlCharacters(terminal.log.mock.calls[0].join('')); + expect(output).not.toMatch(/^(Web|iOS|Android) /); + expect(output).toContain('LOG'); + }); + + it(`does not prefix client logs without mode`, () => { + reporter._log({ + type: 'client_log', + level: 'log', + data: ['hello world'], + } as any); + + expect(terminal.log).toHaveBeenCalledTimes(1); + const output = stripVTControlCharacters(terminal.log.mock.calls[0].join('')); + expect(output).not.toMatch(/^(Web|iOS|Android) /); + }); + + it(`applies printf-style %s format substitution`, () => { + reporter._log({ + type: 'client_log', + level: 'warn', + data: ['%s\n\n%s', 'An error occurred.', 'Visit https://react.dev for more info.'], + mode: 'web', + } as any); + + expect(terminal.log).toHaveBeenCalledTimes(1); + const output = stripVTControlCharacters(terminal.log.mock.calls[0].join('')); + expect(output).not.toContain('%s'); + expect(output).toContain('An error occurred.'); + expect(output).toContain('Visit https://react.dev for more info.'); + }); + + it(`does not apply format substitution when first arg has no specifiers`, () => { + reporter._log({ + type: 'client_log', + level: 'log', + data: ['plain message', 'extra arg'], + } as any); + + expect(terminal.log).toHaveBeenCalledTimes(1); + const args = terminal.log.mock.calls[0]; + // Both items should be passed through as separate arguments + expect(args).toContain('plain message'); + expect(args).toContain('extra arg'); + }); +}); + describe('_getBundleStatusMessage', () => { const buildID = '1'; const reporter = new MetroTerminalReporter('/', { @@ -582,6 +673,153 @@ describe('non-interactive terminal output', () => { }); }); +describe('status cleared before log lines', () => { + beforeEach(() => mockIsInteractive.mockReturnValue(true)); + afterEach(() => mockIsInteractive.mockReturnValue(false)); + + /** + * Metro's Terminal.#update() is async and snapshots #nextStatusStr when it starts. + * When _log() calls terminal.log(), Terminal starts #update() which captures + * whatever status is currently set. If it's a progress bar, that progress bar + * gets written as permanent output between log lines (it can't be reliably + * cleared before more output arrives). + * + * The fix: update() clears the status to empty before _log() runs, so Terminal's + * #update() captures an empty status and writes no progress bars alongside log lines. + */ + + const buildID1 = 'build-1'; + const buildID2 = 'build-2'; + const entryFile = 'node_modules/expo-router/entry.js'; + const serverEntry = 'packages/@expo/router-server/node/render.js'; + const webDetails = asBundleDetails({ + entryFile, + platform: 'web', + bundleType: 'bundle', + }); + const serverDetails = asBundleDetails({ + entryFile: serverEntry, + platform: 'web', + bundleType: 'bundle', + customTransformOptions: { environment: 'node' }, + }); + + function createReporter() { + const callOrder: { type: 'log' | 'status'; content: string }[] = []; + + const terminal = { + log: jest.fn((...args: any[]) => { + const content = stripAnsi(args.join(' ')); + callOrder.push({ type: 'log', content }); + }), + persistStatus: jest.fn(), + status: jest.fn((...args: any[]) => { + const content = stripAnsi(args.join(' ')); + callOrder.push({ type: 'status', content }); + }), + flush: jest.fn(), + } satisfies Partial; + + const reporter = new MetroTerminalReporter('/', terminal as any); + reporter._getElapsedTime = jest.fn(() => BigInt(100_000_000)); + return { reporter, terminal, callOrder }; + } + + it('clears status before any log call during bundle_build_done', () => { + const { reporter, callOrder } = createReporter(); + + // Start a bundle and add progress + reporter.update({ + type: 'bundle_build_started', + buildID: buildID1, + bundleDetails: { ...webDetails, buildID: buildID1 }, + isPrefetch: false, + } as any); + reporter.update({ + type: 'bundle_transform_progressed_throttled', + buildID: buildID1, + transformedFileCount: 50, + totalFileCount: 100, + } as any); + + callOrder.length = 0; + + // Bundle completes + reporter.update({ + type: 'bundle_build_done', + buildID: buildID1, + bundleDetails: { ...webDetails, buildID: buildID1 }, + } as any); + + // The FIRST call should be status('') to clear progress bars + expect(callOrder[0]).toEqual({ type: 'status', content: '' }); + + // The log should come after the clear + const bundledLog = callOrder.find((c) => c.type === 'log' && c.content.includes('Bundled')); + expect(bundledLog).toBeDefined(); + }); + + it('clears status before server log warnings', () => { + const { reporter, callOrder } = createReporter(); + + // Start a bundle (web) and add progress + reporter.update({ + type: 'bundle_build_started', + buildID: buildID1, + bundleDetails: { ...webDetails, buildID: buildID1 }, + isPrefetch: false, + } as any); + reporter.update({ + type: 'bundle_transform_progressed_throttled', + buildID: buildID1, + transformedFileCount: 883, + totalFileCount: 886, + } as any); + + callOrder.length = 0; + + // A server log (WARN) comes in while web bundle is still active + reporter.update({ + type: 'unstable_server_log', + level: 'warn', + data: ['Deep imports from react-native are deprecated'], + } as any); + + // The FIRST call should be status('') to clear the web progress bar + expect(callOrder[0]).toEqual({ type: 'status', content: '' }); + + // Status should be restored at the end (web bundle still active) + const lastStatus = [...callOrder].reverse().find((c) => c.type === 'status'); + expect(lastStatus?.content).toContain(entryFile); + }); + + it('does not clear status for progress events', () => { + const { reporter, callOrder } = createReporter(); + + // Start a bundle + reporter.update({ + type: 'bundle_build_started', + buildID: buildID1, + bundleDetails: { ...webDetails, buildID: buildID1 }, + isPrefetch: false, + } as any); + + callOrder.length = 0; + + // Progress event should NOT clear the status + reporter.update({ + type: 'bundle_transform_progressed_throttled', + buildID: buildID1, + transformedFileCount: 50, + totalFileCount: 100, + } as any); + + // Should not start with a clear + const firstStatus = callOrder.find((c) => c.type === 'status'); + expect(firstStatus?.content).not.toBe(''); + }); +}); + describe('extractCodeFrame', () => { it('extracts code frame from a message', () => { const inputMessage = ` diff --git a/packages/@expo/cli/src/start/server/metro/instantiateMetro.ts b/packages/@expo/cli/src/start/server/metro/instantiateMetro.ts index 041211d213b84e..f92432d932f672 100644 --- a/packages/@expo/cli/src/start/server/metro/instantiateMetro.ts +++ b/packages/@expo/cli/src/start/server/metro/instantiateMetro.ts @@ -84,8 +84,20 @@ function asWritable(input: T): { -readonly [K in keyof T]: T[K] } { return input; } -// Wrap terminal and polyfill console.log so we can log during bundling without breaking the indicator. +/** + * Extends Metro's Terminal to intercept all console methods so they don't + * corrupt the progress bar status lines. + * + * console.log/info are routed through terminal.log() (stdout, managed). + * console.warn/error are routed through logStderr() which clears the + * status from stdout before writing to stderr, then restores it. + * Without this, unmanaged stderr writes shift the cursor and cause + * progress bars to get stuck as permanent output. + */ class LogRespectingTerminal extends Terminal { + #stderrQueue: string[] = []; + #drainingStderr = false; + constructor(stream: import('node:net').Socket | import('node:stream').Writable) { super(stream, { ttyPrint: true }); @@ -100,8 +112,49 @@ class LogRespectingTerminal extends Terminal { this.flush(); }; + const sendStderr = (...msg: any[]) => { + if (!msg.length) { + this.logStderr(''); + } else { + const [format, ...args] = msg; + this.logStderr(require('util').format(format, ...args)); + } + }; + console.log = sendLog; console.info = sendLog; + console.warn = sendStderr; + console.error = sendStderr; + } + + /** Write to stderr without corrupting Terminal's cursor tracking. */ + logStderr(line: string): void { + if (!(process.stdout as any).isTTY) { + process.stderr.write(line + '\n'); + return; + } + this.#stderrQueue.push(line); + this.#drainStderr(); + } + + async #drainStderr(): Promise { + if (this.#drainingStderr) return; + this.#drainingStderr = true; + + while (this.#stderrQueue.length > 0) { + // Clear status, flush to ensure it's removed from screen + const prev = this.status(''); + await this.flush(); + + // Write to stderr while status is cleared + const lines = this.#stderrQueue.splice(0); + process.stderr.write(lines.join('\n') + '\n'); + + // Restore status + this.status(prev); + } + + this.#drainingStderr = false; } } diff --git a/packages/@expo/cli/src/start/server/metro/runServer-fork.ts b/packages/@expo/cli/src/start/server/metro/runServer-fork.ts index ec738fe621ca6f..96b1000013f0e3 100644 --- a/packages/@expo/cli/src/start/server/metro/runServer-fork.ts +++ b/packages/@expo/cli/src/start/server/metro/runServer-fork.ts @@ -13,7 +13,6 @@ import assert from 'assert'; import http from 'http'; import https from 'https'; import type { AddressInfo } from 'net'; -import { parse } from 'url'; import type { WebSocketServer } from 'ws'; import type { MetroBundlerDevServer } from './MetroBundlerDevServer'; @@ -171,7 +170,7 @@ export const runServer = async ( }); httpServer.on('upgrade', (request, socket, head) => { - const { pathname } = parse(request.url!); + const { pathname } = new URL(request.url!, 'http://localhost'); if (pathname != null && websocketEndpoints[pathname]) { websocketEndpoints[pathname].handleUpgrade(request, socket, head, (ws) => { websocketEndpoints[pathname]?.emit('connection', ws, request); diff --git a/packages/@expo/cli/src/start/server/serverLogLikeMetro.ts b/packages/@expo/cli/src/start/server/serverLogLikeMetro.ts index 2a702a3a5bd34f..1280288fe181de 100644 --- a/packages/@expo/cli/src/start/server/serverLogLikeMetro.ts +++ b/packages/@expo/cli/src/start/server/serverLogLikeMetro.ts @@ -40,7 +40,7 @@ let collapsedGuardTimer: ReturnType | undefined; export function logLikeMetro( originalLogFunction: (...args: any[]) => void, level: ConsoleMethod, - platform: 'BRIDGE' | 'NOBRIDGE' | 'λ' | null, + platform: string | null, ...data: any[] ) { const logFunction = console[level] && level !== 'trace' ? level : 'log'; diff --git a/packages/@expo/log-box/CHANGELOG.md b/packages/@expo/log-box/CHANGELOG.md index f7fbb232846afc..7c531ecf87e40f 100644 --- a/packages/@expo/log-box/CHANGELOG.md +++ b/packages/@expo/log-box/CHANGELOG.md @@ -8,6 +8,8 @@ ### 🐛 Bug fixes +- Fix error overlay footer overlapping content by using fixed positioning and adding bottom padding. ([#45526](https://github.com/expo/expo/pull/45526) by [@EvanBacon](https://github.com/EvanBacon)) + ### 💡 Others ## 56.0.5 — 2026-05-07 diff --git a/packages/@expo/log-box/src/overlay/Overlay.module.css b/packages/@expo/log-box/src/overlay/Overlay.module.css index d5f524ad67bd56..668d1f639aff35 100644 --- a/packages/@expo/log-box/src/overlay/Overlay.module.css +++ b/packages/@expo/log-box/src/overlay/Overlay.module.css @@ -150,7 +150,7 @@ } .footer { - position: absolute; + position: fixed; bottom: 0; left: 0; right: 0; diff --git a/packages/@expo/log-box/src/overlay/Overlay.tsx b/packages/@expo/log-box/src/overlay/Overlay.tsx index 988fdc21aea5e8..ef8e76f8bf4b5c 100644 --- a/packages/@expo/log-box/src/overlay/Overlay.tsx +++ b/packages/@expo/log-box/src/overlay/Overlay.tsx @@ -293,7 +293,8 @@ function LogBoxContent({ /> { -
+
{codeFrames.map(([key, codeFrame]) => { // If no frame from a stack is expanded, likely no frame is from user code, let's not show the code snippet. // This avoid cluttering the overlay with irrelevant code frames of node_modules and internals. diff --git a/packages/@expo/metro-runtime/CHANGELOG.md b/packages/@expo/metro-runtime/CHANGELOG.md index ec6b54132e2de2..73c93a8398d0ca 100644 --- a/packages/@expo/metro-runtime/CHANGELOG.md +++ b/packages/@expo/metro-runtime/CHANGELOG.md @@ -10,6 +10,8 @@ ### 💡 Others +- Remove pinned dependencies ([#45520](https://github.com/expo/expo/pull/45520) by [@kitten](https://githun.com/kitten)) + ## 56.0.4 — 2026-05-06 _This version does not introduce any user-facing changes._ diff --git a/packages/@expo/metro-runtime/package.json b/packages/@expo/metro-runtime/package.json index 24dc106efc8ec4..33c75c4000ab1f 100644 --- a/packages/@expo/metro-runtime/package.json +++ b/packages/@expo/metro-runtime/package.json @@ -38,6 +38,7 @@ "url": "https://github.com/expo/expo.git" }, "peerDependencies": { + "@expo/log-box": "workspace:^56.0.5", "expo": "*", "react": "*", "react-dom": "*", @@ -49,7 +50,7 @@ } }, "dependencies": { - "@expo/log-box": "workspace:56.0.5", + "@expo/log-box": "workspace:^56.0.5", "anser": "^1.4.9", "stacktrace-parser": "^0.1.10", "pretty-format": "^29.7.0", diff --git a/packages/@expo/prebuild-config/CHANGELOG.md b/packages/@expo/prebuild-config/CHANGELOG.md index 5128d34d02d1de..d0c040770a72e1 100644 --- a/packages/@expo/prebuild-config/CHANGELOG.md +++ b/packages/@expo/prebuild-config/CHANGELOG.md @@ -8,6 +8,8 @@ ### 🐛 Bug fixes +- Remove unnecessary warning when no icon is defined in the Expo config. ([#45515](https://github.com/expo/expo/pull/45515) by [@EvanBacon](https://github.com/EvanBacon)) + ### 💡 Others ## 56.0.3 — 2026-05-06 diff --git a/packages/@expo/prebuild-config/build/plugins/icons/withIosIcons.js b/packages/@expo/prebuild-config/build/plugins/icons/withIosIcons.js index cc56c0418448f7..4bb832b78ce3b6 100644 --- a/packages/@expo/prebuild-config/build/plugins/icons/withIosIcons.js +++ b/packages/@expo/prebuild-config/build/plugins/icons/withIosIcons.js @@ -107,9 +107,6 @@ function getIcons(config) { } async function setIconsAsync(config, projectRoot) { const icon = getIcons(config); - if (!icon || typeof icon === 'string' && !icon || typeof icon === 'object' && !icon?.light && !icon?.dark && !icon?.tinted) { - _configPlugins().WarningAggregator.addWarningIOS('icon', 'No icon is defined in the Expo config.'); - } // Something like projectRoot/ios/MyApp/ const iosNamedProjectRoot = getIosNamedProjectPath(projectRoot); diff --git a/packages/@expo/prebuild-config/build/plugins/icons/withIosIcons.js.map b/packages/@expo/prebuild-config/build/plugins/icons/withIosIcons.js.map index d6ce0f0d50332c..f80481b0076d77 100644 --- a/packages/@expo/prebuild-config/build/plugins/icons/withIosIcons.js.map +++ b/packages/@expo/prebuild-config/build/plugins/icons/withIosIcons.js.map @@ -1 +1 @@ -{"version":3,"file":"withIosIcons.js","names":["_configPlugins","data","require","_Target","_imageUtils","_fs","_interopRequireDefault","_path","_AssetContents","e","__esModule","default","getProjectName","IOSConfig","XcodeUtils","IMAGE_CACHE_NAME","IMAGESET_PATH","withIosIcons","config","withDangerousMod","setIconsAsync","modRequest","projectRoot","withXcodeProject","icon","getIcons","projectName","path","extname","iconName","basename","setIconName","modResults","addIconFileToProject","exports","iosSpecificIcons","ios","paths","light","dark","tinted","filter","Boolean","iconPath","WarningAggregator","addWarningIOS","iosNamedProjectRoot","getIosNamedProjectPath","addLiquidGlassIcon","fs","promises","mkdir","join","recursive","imagesJson","baseIconPath","baseIcon","generateUniversalIconAsync","cacheKey","platform","push","darkIcon","appearance","tintedIcon","writeContentsJsonAsync","images","getAppleIconName","size","scale","name","filename","source","generateImageAsync","cacheType","src","width","height","removeTransparency","resizeMode","backgroundColor","undefined","createSquareAsync","assetPath","writeFile","idiom","appearances","value","sourceIconPath","targetIconPath","existsSync","cp","project","target","findNativeTargetByName","configurations","getBuildConfigurationsForListId","buildConfigurationList","buildSettings","ASSETCATALOG_COMPILER_APPICON_NAME","addResourceFileToGroup","filepath","groupName","isBuildFile","verbose"],"sources":["../../../src/plugins/icons/withIosIcons.ts"],"sourcesContent":["import type { ConfigPlugin } from '@expo/config-plugins';\nimport {\n IOSConfig,\n WarningAggregator,\n withDangerousMod,\n withXcodeProject,\n} from '@expo/config-plugins';\nimport { findNativeTargetByName } from '@expo/config-plugins/build/ios/Target';\nimport type { ExpoConfig, IOSIcons } from '@expo/config-types';\nimport { createSquareAsync, generateImageAsync } from '@expo/image-utils';\nimport fs from 'fs';\nimport path from 'path';\n\nimport type { ContentsJson, ContentsJsonImage } from './AssetContents';\nimport { writeContentsJsonAsync } from './AssetContents';\n\nconst { getProjectName } = IOSConfig.XcodeUtils;\n\nconst IMAGE_CACHE_NAME = 'icons';\nconst IMAGESET_PATH = 'Images.xcassets/AppIcon.appiconset';\n\nexport const withIosIcons: ConfigPlugin = (config) => {\n config = withDangerousMod(config, [\n 'ios',\n async (config) => {\n await setIconsAsync(config, config.modRequest.projectRoot);\n return config;\n },\n ]);\n\n config = withXcodeProject(config, (config) => {\n const icon = getIcons(config);\n const projectName = config.modRequest.projectName;\n\n if (icon && typeof icon === 'string' && path.extname(icon) === '.icon' && projectName) {\n const iconName = path.basename(icon, '.icon');\n setIconName(config.modResults, projectName, iconName);\n addIconFileToProject(config.modResults, projectName, iconName);\n }\n return config;\n });\n\n return config;\n};\n\nexport function getIcons(config: Pick): IOSIcons | string | null {\n const iosSpecificIcons = config.ios?.icon;\n\n if (iosSpecificIcons) {\n // For backwards compatibility, the icon can be a string\n if (typeof iosSpecificIcons === 'string') {\n return iosSpecificIcons || config.icon || null;\n }\n\n if (typeof iosSpecificIcons === 'object') {\n const paths = [iosSpecificIcons.light, iosSpecificIcons.dark, iosSpecificIcons.tinted].filter(\n Boolean\n );\n for (const iconPath of paths) {\n if (typeof iconPath === 'string' && path.extname(iconPath) === '.icon') {\n WarningAggregator.addWarningIOS(\n 'icon',\n `Liquid glass icons (.icon) should be provided as a string to the \"ios.icon\" property, not as an object. Found: \"${iconPath}\"`\n );\n }\n }\n }\n\n // in iOS 18 introduced the ability to specify dark and tinted icons, which users can specify as an object\n if (!iosSpecificIcons.light && !iosSpecificIcons.dark && !iosSpecificIcons.tinted) {\n return config.icon || null;\n }\n\n return iosSpecificIcons;\n }\n\n // Top level icon property should not be used to specify a `.icon` folder\n if (config.icon && typeof config.icon === 'string' && path.extname(config.icon) === '.icon') {\n WarningAggregator.addWarningIOS(\n 'icon',\n `Liquid glass icons (.icon) should be provided via the \"ios.icon\" property, not the root \"icon\" property. Found: \"${config.icon}\"`\n );\n }\n\n if (config.icon) {\n return config.icon;\n }\n\n return null;\n}\n\nexport async function setIconsAsync(config: ExpoConfig, projectRoot: string) {\n const icon = getIcons(config);\n\n if (\n !icon ||\n (typeof icon === 'string' && !icon) ||\n (typeof icon === 'object' && !icon?.light && !icon?.dark && !icon?.tinted)\n ) {\n WarningAggregator.addWarningIOS('icon', 'No icon is defined in the Expo config.');\n }\n\n // Something like projectRoot/ios/MyApp/\n const iosNamedProjectRoot = getIosNamedProjectPath(projectRoot);\n\n if (typeof icon === 'string' && path.extname(icon) === '.icon') {\n return await addLiquidGlassIcon(icon, projectRoot, iosNamedProjectRoot);\n }\n\n // Ensure the Images.xcassets/AppIcon.appiconset path exists\n await fs.promises.mkdir(path.join(iosNamedProjectRoot, IMAGESET_PATH), { recursive: true });\n\n const imagesJson: ContentsJson['images'] = [];\n\n const baseIconPath = typeof icon === 'object' ? icon?.light || icon?.dark || icon?.tinted : icon;\n\n // Store the image JSON data for assigning via the Contents.json\n const baseIcon = await generateUniversalIconAsync(projectRoot, {\n icon: baseIconPath,\n cacheKey: 'universal-icon',\n iosNamedProjectRoot,\n platform: 'ios',\n });\n\n imagesJson.push(baseIcon);\n\n if (typeof icon === 'object') {\n if (icon?.dark) {\n const darkIcon = await generateUniversalIconAsync(projectRoot, {\n icon: icon.dark,\n cacheKey: 'universal-icon-dark',\n iosNamedProjectRoot,\n platform: 'ios',\n appearance: 'dark',\n });\n\n imagesJson.push(darkIcon);\n }\n\n if (icon?.tinted) {\n const tintedIcon = await generateUniversalIconAsync(projectRoot, {\n icon: icon.tinted,\n cacheKey: 'universal-icon-tinted',\n iosNamedProjectRoot,\n platform: 'ios',\n appearance: 'tinted',\n });\n\n imagesJson.push(tintedIcon);\n }\n }\n\n // Finally, write the Contents.json\n await writeContentsJsonAsync(path.join(iosNamedProjectRoot, IMAGESET_PATH), {\n images: imagesJson,\n });\n}\n\n/**\n * Return the project's named iOS path: ios/MyProject/\n *\n * @param projectRoot Expo project root path.\n */\nfunction getIosNamedProjectPath(projectRoot: string): string {\n const projectName = getProjectName(projectRoot);\n return path.join(projectRoot, 'ios', projectName);\n}\n\nfunction getAppleIconName(size: number, scale: number, appearance?: 'dark' | 'tinted'): string {\n let name = 'App-Icon';\n\n if (appearance) {\n name = `${name}-${appearance}`;\n }\n\n name = `${name}-${size}x${size}@${scale}x.png`;\n\n return name;\n}\n\nexport async function generateUniversalIconAsync(\n projectRoot: string,\n {\n icon,\n cacheKey,\n iosNamedProjectRoot,\n platform,\n appearance,\n }: {\n platform: 'watchos' | 'ios';\n icon?: string | null;\n appearance?: 'dark' | 'tinted';\n iosNamedProjectRoot: string;\n cacheKey: string;\n }\n): Promise {\n const size = 1024;\n const filename = getAppleIconName(size, 1, appearance);\n\n let source: Buffer;\n\n if (icon) {\n // Using this method will cache the images in `.expo` based on the properties used to generate them.\n // this method also supports remote URLs and using the global sharp instance.\n source = (\n await generateImageAsync(\n { projectRoot, cacheType: IMAGE_CACHE_NAME + cacheKey },\n {\n src: icon,\n name: filename,\n width: size,\n height: size,\n // Transparency needs to be preserved in dark variant, but can safely be removed in \"light\" and \"tinted\" variants.\n removeTransparency: appearance !== 'dark',\n // The icon should be square, but if it's not then it will be cropped.\n resizeMode: 'cover',\n // Force the background color to solid white to prevent any transparency. (for \"any\" and \"tinted\" variants)\n // TODO: Maybe use a more adaptive option based on the icon color?\n backgroundColor: appearance !== 'dark' ? '#ffffff' : undefined,\n }\n )\n ).source;\n } else {\n // Create a white square image if no icon exists to mitigate the chance of a submission failure to the app store.\n source = await createSquareAsync({ size });\n }\n // Write image buffer to the file system.\n const assetPath = path.join(iosNamedProjectRoot, IMAGESET_PATH, filename);\n await fs.promises.writeFile(assetPath, source);\n\n return {\n filename,\n idiom: 'universal',\n platform,\n size: `${size}x${size}`,\n ...(appearance ? { appearances: [{ appearance: 'luminosity', value: appearance }] } : {}),\n };\n}\n\nasync function addLiquidGlassIcon(\n iconPath: string,\n projectRoot: string,\n iosNamedProjectRoot: string\n): Promise {\n const iconName = path.basename(iconPath, '.icon');\n const sourceIconPath = path.join(projectRoot, iconPath);\n const targetIconPath = path.join(iosNamedProjectRoot, `${iconName}.icon`);\n\n if (!fs.existsSync(sourceIconPath)) {\n WarningAggregator.addWarningIOS(\n 'icon',\n `Liquid glass icon file not found at path: ${iconPath}`\n );\n return;\n }\n\n await fs.promises.cp(sourceIconPath, targetIconPath, { recursive: true });\n}\n\n/**\n * Adds the .icons name to the project\n */\nfunction setIconName(project: any, projectName: string, iconName: string): void {\n const [, target] = findNativeTargetByName(project, projectName);\n const configurations = IOSConfig.XcodeUtils.getBuildConfigurationsForListId(\n project,\n target.buildConfigurationList\n );\n\n for (const [, config] of configurations) {\n if ((config as any)?.buildSettings) {\n (config as any).buildSettings.ASSETCATALOG_COMPILER_APPICON_NAME = iconName;\n }\n }\n}\n\n/**\n * Adds the .icon file to the project\n */\nfunction addIconFileToProject(project: any, projectName: string, iconName: string): void {\n const iconPath = `${iconName}.icon`;\n\n IOSConfig.XcodeUtils.addResourceFileToGroup({\n filepath: `${projectName}/${iconPath}`,\n groupName: projectName,\n project,\n isBuildFile: true,\n verbose: true,\n });\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"withIosIcons.js","names":["_configPlugins","data","require","_Target","_imageUtils","_fs","_interopRequireDefault","_path","_AssetContents","e","__esModule","default","getProjectName","IOSConfig","XcodeUtils","IMAGE_CACHE_NAME","IMAGESET_PATH","withIosIcons","config","withDangerousMod","setIconsAsync","modRequest","projectRoot","withXcodeProject","icon","getIcons","projectName","path","extname","iconName","basename","setIconName","modResults","addIconFileToProject","exports","iosSpecificIcons","ios","paths","light","dark","tinted","filter","Boolean","iconPath","WarningAggregator","addWarningIOS","iosNamedProjectRoot","getIosNamedProjectPath","addLiquidGlassIcon","fs","promises","mkdir","join","recursive","imagesJson","baseIconPath","baseIcon","generateUniversalIconAsync","cacheKey","platform","push","darkIcon","appearance","tintedIcon","writeContentsJsonAsync","images","getAppleIconName","size","scale","name","filename","source","generateImageAsync","cacheType","src","width","height","removeTransparency","resizeMode","backgroundColor","undefined","createSquareAsync","assetPath","writeFile","idiom","appearances","value","sourceIconPath","targetIconPath","existsSync","cp","project","target","findNativeTargetByName","configurations","getBuildConfigurationsForListId","buildConfigurationList","buildSettings","ASSETCATALOG_COMPILER_APPICON_NAME","addResourceFileToGroup","filepath","groupName","isBuildFile","verbose"],"sources":["../../../src/plugins/icons/withIosIcons.ts"],"sourcesContent":["import type { ConfigPlugin } from '@expo/config-plugins';\nimport {\n IOSConfig,\n WarningAggregator,\n withDangerousMod,\n withXcodeProject,\n} from '@expo/config-plugins';\nimport { findNativeTargetByName } from '@expo/config-plugins/build/ios/Target';\nimport type { ExpoConfig, IOSIcons } from '@expo/config-types';\nimport { createSquareAsync, generateImageAsync } from '@expo/image-utils';\nimport fs from 'fs';\nimport path from 'path';\n\nimport type { ContentsJson, ContentsJsonImage } from './AssetContents';\nimport { writeContentsJsonAsync } from './AssetContents';\n\nconst { getProjectName } = IOSConfig.XcodeUtils;\n\nconst IMAGE_CACHE_NAME = 'icons';\nconst IMAGESET_PATH = 'Images.xcassets/AppIcon.appiconset';\n\nexport const withIosIcons: ConfigPlugin = (config) => {\n config = withDangerousMod(config, [\n 'ios',\n async (config) => {\n await setIconsAsync(config, config.modRequest.projectRoot);\n return config;\n },\n ]);\n\n config = withXcodeProject(config, (config) => {\n const icon = getIcons(config);\n const projectName = config.modRequest.projectName;\n\n if (icon && typeof icon === 'string' && path.extname(icon) === '.icon' && projectName) {\n const iconName = path.basename(icon, '.icon');\n setIconName(config.modResults, projectName, iconName);\n addIconFileToProject(config.modResults, projectName, iconName);\n }\n return config;\n });\n\n return config;\n};\n\nexport function getIcons(config: Pick): IOSIcons | string | null {\n const iosSpecificIcons = config.ios?.icon;\n\n if (iosSpecificIcons) {\n // For backwards compatibility, the icon can be a string\n if (typeof iosSpecificIcons === 'string') {\n return iosSpecificIcons || config.icon || null;\n }\n\n if (typeof iosSpecificIcons === 'object') {\n const paths = [iosSpecificIcons.light, iosSpecificIcons.dark, iosSpecificIcons.tinted].filter(\n Boolean\n );\n for (const iconPath of paths) {\n if (typeof iconPath === 'string' && path.extname(iconPath) === '.icon') {\n WarningAggregator.addWarningIOS(\n 'icon',\n `Liquid glass icons (.icon) should be provided as a string to the \"ios.icon\" property, not as an object. Found: \"${iconPath}\"`\n );\n }\n }\n }\n\n // in iOS 18 introduced the ability to specify dark and tinted icons, which users can specify as an object\n if (!iosSpecificIcons.light && !iosSpecificIcons.dark && !iosSpecificIcons.tinted) {\n return config.icon || null;\n }\n\n return iosSpecificIcons;\n }\n\n // Top level icon property should not be used to specify a `.icon` folder\n if (config.icon && typeof config.icon === 'string' && path.extname(config.icon) === '.icon') {\n WarningAggregator.addWarningIOS(\n 'icon',\n `Liquid glass icons (.icon) should be provided via the \"ios.icon\" property, not the root \"icon\" property. Found: \"${config.icon}\"`\n );\n }\n\n if (config.icon) {\n return config.icon;\n }\n\n return null;\n}\n\nexport async function setIconsAsync(config: ExpoConfig, projectRoot: string) {\n const icon = getIcons(config);\n\n // Something like projectRoot/ios/MyApp/\n const iosNamedProjectRoot = getIosNamedProjectPath(projectRoot);\n\n if (typeof icon === 'string' && path.extname(icon) === '.icon') {\n return await addLiquidGlassIcon(icon, projectRoot, iosNamedProjectRoot);\n }\n\n // Ensure the Images.xcassets/AppIcon.appiconset path exists\n await fs.promises.mkdir(path.join(iosNamedProjectRoot, IMAGESET_PATH), { recursive: true });\n\n const imagesJson: ContentsJson['images'] = [];\n\n const baseIconPath = typeof icon === 'object' ? icon?.light || icon?.dark || icon?.tinted : icon;\n\n // Store the image JSON data for assigning via the Contents.json\n const baseIcon = await generateUniversalIconAsync(projectRoot, {\n icon: baseIconPath,\n cacheKey: 'universal-icon',\n iosNamedProjectRoot,\n platform: 'ios',\n });\n\n imagesJson.push(baseIcon);\n\n if (typeof icon === 'object') {\n if (icon?.dark) {\n const darkIcon = await generateUniversalIconAsync(projectRoot, {\n icon: icon.dark,\n cacheKey: 'universal-icon-dark',\n iosNamedProjectRoot,\n platform: 'ios',\n appearance: 'dark',\n });\n\n imagesJson.push(darkIcon);\n }\n\n if (icon?.tinted) {\n const tintedIcon = await generateUniversalIconAsync(projectRoot, {\n icon: icon.tinted,\n cacheKey: 'universal-icon-tinted',\n iosNamedProjectRoot,\n platform: 'ios',\n appearance: 'tinted',\n });\n\n imagesJson.push(tintedIcon);\n }\n }\n\n // Finally, write the Contents.json\n await writeContentsJsonAsync(path.join(iosNamedProjectRoot, IMAGESET_PATH), {\n images: imagesJson,\n });\n}\n\n/**\n * Return the project's named iOS path: ios/MyProject/\n *\n * @param projectRoot Expo project root path.\n */\nfunction getIosNamedProjectPath(projectRoot: string): string {\n const projectName = getProjectName(projectRoot);\n return path.join(projectRoot, 'ios', projectName);\n}\n\nfunction getAppleIconName(size: number, scale: number, appearance?: 'dark' | 'tinted'): string {\n let name = 'App-Icon';\n\n if (appearance) {\n name = `${name}-${appearance}`;\n }\n\n name = `${name}-${size}x${size}@${scale}x.png`;\n\n return name;\n}\n\nexport async function generateUniversalIconAsync(\n projectRoot: string,\n {\n icon,\n cacheKey,\n iosNamedProjectRoot,\n platform,\n appearance,\n }: {\n platform: 'watchos' | 'ios';\n icon?: string | null;\n appearance?: 'dark' | 'tinted';\n iosNamedProjectRoot: string;\n cacheKey: string;\n }\n): Promise {\n const size = 1024;\n const filename = getAppleIconName(size, 1, appearance);\n\n let source: Buffer;\n\n if (icon) {\n // Using this method will cache the images in `.expo` based on the properties used to generate them.\n // this method also supports remote URLs and using the global sharp instance.\n source = (\n await generateImageAsync(\n { projectRoot, cacheType: IMAGE_CACHE_NAME + cacheKey },\n {\n src: icon,\n name: filename,\n width: size,\n height: size,\n // Transparency needs to be preserved in dark variant, but can safely be removed in \"light\" and \"tinted\" variants.\n removeTransparency: appearance !== 'dark',\n // The icon should be square, but if it's not then it will be cropped.\n resizeMode: 'cover',\n // Force the background color to solid white to prevent any transparency. (for \"any\" and \"tinted\" variants)\n // TODO: Maybe use a more adaptive option based on the icon color?\n backgroundColor: appearance !== 'dark' ? '#ffffff' : undefined,\n }\n )\n ).source;\n } else {\n // Create a white square image if no icon exists to mitigate the chance of a submission failure to the app store.\n source = await createSquareAsync({ size });\n }\n // Write image buffer to the file system.\n const assetPath = path.join(iosNamedProjectRoot, IMAGESET_PATH, filename);\n await fs.promises.writeFile(assetPath, source);\n\n return {\n filename,\n idiom: 'universal',\n platform,\n size: `${size}x${size}`,\n ...(appearance ? { appearances: [{ appearance: 'luminosity', value: appearance }] } : {}),\n };\n}\n\nasync function addLiquidGlassIcon(\n iconPath: string,\n projectRoot: string,\n iosNamedProjectRoot: string\n): Promise {\n const iconName = path.basename(iconPath, '.icon');\n const sourceIconPath = path.join(projectRoot, iconPath);\n const targetIconPath = path.join(iosNamedProjectRoot, `${iconName}.icon`);\n\n if (!fs.existsSync(sourceIconPath)) {\n WarningAggregator.addWarningIOS(\n 'icon',\n `Liquid glass icon file not found at path: ${iconPath}`\n );\n return;\n }\n\n await fs.promises.cp(sourceIconPath, targetIconPath, { recursive: true });\n}\n\n/**\n * Adds the .icons name to the project\n */\nfunction setIconName(project: any, projectName: string, iconName: string): void {\n const [, target] = findNativeTargetByName(project, projectName);\n const configurations = IOSConfig.XcodeUtils.getBuildConfigurationsForListId(\n project,\n target.buildConfigurationList\n );\n\n for (const [, config] of configurations) {\n if ((config as any)?.buildSettings) {\n (config as any).buildSettings.ASSETCATALOG_COMPILER_APPICON_NAME = iconName;\n }\n }\n}\n\n/**\n * Adds the .icon file to the project\n */\nfunction addIconFileToProject(project: any, projectName: string, iconName: string): void {\n const iconPath = `${iconName}.icon`;\n\n IOSConfig.XcodeUtils.addResourceFileToGroup({\n filepath: `${projectName}/${iconPath}`,\n groupName: projectName,\n project,\n isBuildFile: true,\n verbose: true,\n });\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/packages/@expo/prebuild-config/src/plugins/icons/__tests__/withIosIcons-test.ts b/packages/@expo/prebuild-config/src/plugins/icons/__tests__/withIosIcons-test.ts index a5be22bdbca56d..e62ff56b20d426 100644 --- a/packages/@expo/prebuild-config/src/plugins/icons/__tests__/withIosIcons-test.ts +++ b/packages/@expo/prebuild-config/src/plugins/icons/__tests__/withIosIcons-test.ts @@ -198,11 +198,6 @@ describe('e2e: iOS icons with fallback image', () => { value.startsWith('ios/HelloWorld/Images.xcassets/AppIcon.appiconset/App-Icon') ); - expect(WarningAggregator.addWarningIOS).toHaveBeenCalledTimes(1); - expect(WarningAggregator.addWarningIOS).toHaveBeenCalledWith( - 'icon', - 'No icon is defined in the Expo config.' - ); expect(icons.length).toBe(1); // Test the Contents.json file diff --git a/packages/@expo/prebuild-config/src/plugins/icons/withIosIcons.ts b/packages/@expo/prebuild-config/src/plugins/icons/withIosIcons.ts index 1efc28a50448df..4a6cfbcc81f1f1 100644 --- a/packages/@expo/prebuild-config/src/plugins/icons/withIosIcons.ts +++ b/packages/@expo/prebuild-config/src/plugins/icons/withIosIcons.ts @@ -92,14 +92,6 @@ export function getIcons(config: Pick): IOSIcons | s export async function setIconsAsync(config: ExpoConfig, projectRoot: string) { const icon = getIcons(config); - if ( - !icon || - (typeof icon === 'string' && !icon) || - (typeof icon === 'object' && !icon?.light && !icon?.dark && !icon?.tinted) - ) { - WarningAggregator.addWarningIOS('icon', 'No icon is defined in the Expo config.'); - } - // Something like projectRoot/ios/MyApp/ const iosNamedProjectRoot = getIosNamedProjectPath(projectRoot); diff --git a/packages/expo-dev-client/CHANGELOG.md b/packages/expo-dev-client/CHANGELOG.md index cca0ec71a7cafa..125e2871264586 100644 --- a/packages/expo-dev-client/CHANGELOG.md +++ b/packages/expo-dev-client/CHANGELOG.md @@ -10,6 +10,8 @@ ### 💡 Others +- Remove pinned dependencies ([#45520](https://github.com/expo/expo/pull/45520) by [@kitten](https://githun.com/kitten)) + ## 56.0.4 — 2026-05-07 _This version does not introduce any user-facing changes._ diff --git a/packages/expo-dev-client/package.json b/packages/expo-dev-client/package.json index 297b3d0e1a90b2..7b9473c12c1118 100644 --- a/packages/expo-dev-client/package.json +++ b/packages/expo-dev-client/package.json @@ -31,9 +31,9 @@ "license": "MIT", "homepage": "https://docs.expo.dev/versions/latest/sdk/dev-client/", "dependencies": { - "expo-dev-launcher": "workspace:56.0.4", - "expo-dev-menu": "workspace:56.0.4", - "expo-dev-menu-interface": "workspace:56.0.0", + "expo-dev-launcher": "workspace:~56.0.4", + "expo-dev-menu": "workspace:~56.0.4", + "expo-dev-menu-interface": "workspace:~56.0.0", "expo-manifests": "workspace:~56.0.1", "expo-updates-interface": "workspace:~56.0.1" }, diff --git a/packages/expo-dev-launcher/CHANGELOG.md b/packages/expo-dev-launcher/CHANGELOG.md index a3835da13bf996..55de66b48a52a1 100644 --- a/packages/expo-dev-launcher/CHANGELOG.md +++ b/packages/expo-dev-launcher/CHANGELOG.md @@ -10,6 +10,8 @@ ### 💡 Others +- Remove pinned dependencies ([#45520](https://github.com/expo/expo/pull/45520) by [@kitten](https://githun.com/kitten)) + ## 56.0.4 — 2026-05-07 _This version does not introduce any user-facing changes._ diff --git a/packages/expo-dev-launcher/package.json b/packages/expo-dev-launcher/package.json index 8b0b30d9227064..7d2d1638d27069 100644 --- a/packages/expo-dev-launcher/package.json +++ b/packages/expo-dev-launcher/package.json @@ -20,7 +20,7 @@ "homepage": "https://docs.expo.dev", "dependencies": { "@expo/schema-utils": "workspace:^56.0.0", - "expo-dev-menu": "workspace:56.0.4", + "expo-dev-menu": "workspace:~56.0.4", "expo-manifests": "workspace:~56.0.1" }, "peerDependencies": { diff --git a/packages/expo-dev-menu/CHANGELOG.md b/packages/expo-dev-menu/CHANGELOG.md index cd06250a0f8aa1..8473c0017a17fa 100644 --- a/packages/expo-dev-menu/CHANGELOG.md +++ b/packages/expo-dev-menu/CHANGELOG.md @@ -10,6 +10,8 @@ ### 💡 Others +- Remove pinned dependencies ([#45520](https://github.com/expo/expo/pull/45520) by [@kitten](https://githun.com/kitten)) + ## 56.0.4 — 2026-05-07 _This version does not introduce any user-facing changes._ diff --git a/packages/expo-dev-menu/package.json b/packages/expo-dev-menu/package.json index c1b0efa8ba431b..08798bafc16698 100644 --- a/packages/expo-dev-menu/package.json +++ b/packages/expo-dev-menu/package.json @@ -32,7 +32,7 @@ "license": "MIT", "homepage": "https://docs.expo.dev", "dependencies": { - "expo-dev-menu-interface": "workspace:56.0.0" + "expo-dev-menu-interface": "workspace:~56.0.0" }, "devDependencies": { "@babel/preset-typescript": "^7.7.4", diff --git a/packages/expo-processing/package.json b/packages/expo-processing/package.json index c56181e4e0f1d4..5e3bb09470a711 100644 --- a/packages/expo-processing/package.json +++ b/packages/expo-processing/package.json @@ -22,7 +22,7 @@ "license": "MIT", "homepage": "https://github.com/expo/expo/tree/main/packages/expo-processing", "dependencies": { - "expo-gl": "workspace:56.0.3" + "expo-gl": "workspace:~56.0.3" }, "peerDependencies": { "expo": "*", diff --git a/packages/expo-router/CHANGELOG.md b/packages/expo-router/CHANGELOG.md index de687fdd59e090..aeefdcbdacd7dc 100644 --- a/packages/expo-router/CHANGELOG.md +++ b/packages/expo-router/CHANGELOG.md @@ -10,6 +10,8 @@ ### 💡 Others +- Remove pinned dependencies ([#45520](https://github.com/expo/expo/pull/45520) by [@kitten](https://githun.com/kitten)) + ## 56.1.0 — 2026-05-07 ### 🎉 New features diff --git a/packages/expo-router/package.json b/packages/expo-router/package.json index 87a7920a5e65f6..7441f250fbd4b9 100644 --- a/packages/expo-router/package.json +++ b/packages/expo-router/package.json @@ -88,7 +88,7 @@ "expo" ], "peerDependencies": { - "@expo/log-box": "workspace:56.0.5", + "@expo/log-box": "workspace:^56.0.5", "@expo/metro-runtime": "workspace:^56.0.4", "@testing-library/react-native": ">= 13.2.0", "expo": "*", @@ -149,6 +149,7 @@ "tsd": "^0.33.0" }, "dependencies": { + "@expo/log-box": "workspace:^56.0.5", "@expo/metro-runtime": "workspace:^56.0.4", "@expo/schema-utils": "workspace:^56.0.0", "@expo/ui": "workspace:^56.0.3", diff --git a/packages/expo/CHANGELOG.md b/packages/expo/CHANGELOG.md index 0b14a9ac6fb048..1d54e398eca095 100644 --- a/packages/expo/CHANGELOG.md +++ b/packages/expo/CHANGELOG.md @@ -10,6 +10,9 @@ ### 💡 Others +- Remove pinned dependencies ([#45520](https://github.com/expo/expo/pull/45520) by [@kitten](https://githun.com/kitten)) +- Send platform as HMR log mode for terminal log prefixing. ([#45516](https://github.com/expo/expo/pull/45516) by [@EvanBacon](https://github.com/EvanBacon)) +- Remove redundant log messages from web HMR setup. ([#45516](https://github.com/expo/expo/pull/45516) by [@EvanBacon](https://github.com/EvanBacon)) ## 56.0.0-preview.6 — 2026-05-07 _This version does not introduce any user-facing changes._ diff --git a/packages/expo/build/async-require/hmr.d.ts.map b/packages/expo/build/async-require/hmr.d.ts.map index 557d8d9f2cd5ef..24f633b1a82c38 100644 --- a/packages/expo/build/async-require/hmr.d.ts.map +++ b/packages/expo/build/async-require/hmr.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../../src/async-require/hmr.ts"],"names":[],"mappings":"AAiCA,KAAK,QAAQ,GACT,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,KAAK,GACL,OAAO,GACP,gBAAgB,GAChB,UAAU,GACV,OAAO,CAAC;AAMZ;;;GAGG;AACH,QAAA,MAAM,SAAS;;;+BAyCc,MAAM;eAMtB,QAAQ,QAAQ,GAAG,EAAE;6BA8CX,MAAM,GAAG;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,gBACpC,MAAM,SACb,MAAM,SACN,MAAM,GAAG,MAAM,yBACC,OAAO,WACtB,MAAM;yBAmHK,OAAO;CA4B7B,CAAC;AA8DF,eAAe,SAAS,CAAC"} \ No newline at end of file +{"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../../src/async-require/hmr.ts"],"names":[],"mappings":"AAiCA,KAAK,QAAQ,GACT,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,KAAK,GACL,OAAO,GACP,gBAAgB,GAChB,UAAU,GACV,OAAO,CAAC;AAMZ;;;GAGG;AACH,QAAA,MAAM,SAAS;;;+BAyCc,MAAM;eAMtB,QAAQ,QAAQ,GAAG,EAAE;6BA6CX,MAAM,GAAG;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,gBACpC,MAAM,SACb,MAAM,SACN,MAAM,GAAG,MAAM,yBACC,OAAO,WACtB,MAAM;yBAmHK,OAAO;CA4B7B,CAAC;AA8DF,eAAe,SAAS,CAAC"} \ No newline at end of file diff --git a/packages/expo/package.json b/packages/expo/package.json index 5404970745e246..d1970972e89389 100644 --- a/packages/expo/package.json +++ b/packages/expo/package.json @@ -75,16 +75,16 @@ "homepage": "https://github.com/expo/expo/tree/main/packages/expo", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "workspace:56.0.6", + "@expo/cli": "workspace:^56.0.6", "@expo/config": "workspace:~56.0.2", "@expo/config-plugins": "workspace:~56.0.2", - "@expo/devtools": "workspace:56.0.2", + "@expo/devtools": "workspace:~56.0.2", "@expo/dom-webview": "workspace:~56.0.4", - "@expo/fingerprint": "workspace:0.17.3", - "@expo/local-build-cache-provider": "workspace:56.0.2", - "@expo/log-box": "workspace:56.0.5", + "@expo/fingerprint": "workspace:^0.17.3", + "@expo/local-build-cache-provider": "workspace:^56.0.2", + "@expo/log-box": "workspace:^56.0.5", "@expo/metro": "~56.0.0", - "@expo/metro-config": "workspace:56.0.4", + "@expo/metro-config": "workspace:~56.0.4", "@expo/vector-icons": "^15.0.2", "@ungap/structured-clone": "^1.3.0", "babel-preset-expo": "workspace:~56.0.4", @@ -93,8 +93,8 @@ "expo-file-system": "workspace:~56.0.3", "expo-font": "workspace:~56.0.3", "expo-keep-awake": "workspace:~56.0.3", - "expo-modules-autolinking": "workspace:56.0.2", - "expo-modules-core": "workspace:56.0.4", + "expo-modules-autolinking": "workspace:~56.0.2", + "expo-modules-core": "workspace:~56.0.4", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", "whatwg-url-minimum": "^0.1.1" diff --git a/packages/expo/src/async-require/hmr.ts b/packages/expo/src/async-require/hmr.ts index c0ea71e36a0bbc..bd226af36bb1c8 100644 --- a/packages/expo/src/async-require/hmr.ts +++ b/packages/expo/src/async-require/hmr.ts @@ -111,8 +111,7 @@ const HMRClient = { const webMetadata = process.env.EXPO_OS === 'web' ? { - platform: 'web', - mode: 'BRIDGE', + mode: typeof window.$$EXPO_INITIAL_PROPS !== 'undefined' ? 'dom' : 'web', } : undefined; hmrClient.send( diff --git a/packages/expo/src/async-require/setupHMR.ts b/packages/expo/src/async-require/setupHMR.ts index 72bfcaca7ea472..aee847525fa67a 100644 --- a/packages/expo/src/async-require/setupHMR.ts +++ b/packages/expo/src/async-require/setupHMR.ts @@ -1,6 +1,6 @@ import HMRClient from './hmr'; -if (typeof window !== 'undefined' && typeof window.$$EXPO_INITIAL_PROPS !== 'undefined') { +if (typeof window !== 'undefined') { // Sets up developer tools for web platforms when running in a webview. This ensures that logs are visible in the terminal. // We assume full control over the console and send JavaScript logs to Metro. const LEVELS = [ @@ -21,9 +21,6 @@ if (typeof window !== 'undefined' && typeof window.$$EXPO_INITIAL_PROPS !== 'und originalFunction.apply(console, args); }; }); - HMRClient.log('log', [`[webview] Logs will also appear in the Safari/Chrome debug console`]); -} else { - HMRClient.log('log', [`[web] Logs will appear in the browser console`]); } // This is called native on native platforms diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62226c5a5de6ea..2829e1146ae3b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -268,7 +268,7 @@ importers: version: link:../../packages/expo-module-scripts jest: specifier: ^29.3.1 - version: 29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) + version: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)) apps/bare-expo/e2e/image-comparison: dependencies: @@ -1792,13 +1792,13 @@ importers: specifier: workspace:^0.9.0 version: link:../image-utils '@expo/inline-modules': - specifier: workspace:0.0.4 + specifier: workspace:^0.0.4 version: link:../inline-modules '@expo/json-file': specifier: workspace:^10.1.0 version: link:../json-file '@expo/log-box': - specifier: workspace:56.0.5 + specifier: workspace:^56.0.5 version: link:../log-box '@expo/metro': specifier: ~56.0.0 @@ -1807,7 +1807,7 @@ importers: specifier: workspace:~56.0.4 version: link:../metro-config '@expo/metro-file-map': - specifier: workspace:56.0.0-2 + specifier: workspace:^56.0.0-2 version: link:../metro-file-map '@expo/osascript': specifier: workspace:^2.5.0 @@ -2655,7 +2655,7 @@ importers: packages/@expo/metro-runtime: dependencies: '@expo/log-box': - specifier: workspace:56.0.5 + specifier: workspace:^56.0.5 version: link:../log-box anser: specifier: ^1.4.9 @@ -3122,7 +3122,7 @@ importers: version: link:../expo-splash-screen jest: specifier: ^29.2.1 - version: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) + version: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)) react-refresh: specifier: ^0.14.2 version: 0.14.2 @@ -3428,7 +3428,7 @@ importers: specifier: ^7.20.0 version: 7.29.2 '@expo/cli': - specifier: workspace:56.0.6 + specifier: workspace:^56.0.6 version: link:../@expo/cli '@expo/config': specifier: workspace:~56.0.2 @@ -3437,25 +3437,25 @@ importers: specifier: workspace:~56.0.2 version: link:../@expo/config-plugins '@expo/devtools': - specifier: workspace:56.0.2 + specifier: workspace:~56.0.2 version: link:../@expo/devtools '@expo/dom-webview': specifier: workspace:~56.0.4 version: link:../@expo/dom-webview '@expo/fingerprint': - specifier: workspace:0.17.3 + specifier: workspace:^0.17.3 version: link:../@expo/fingerprint '@expo/local-build-cache-provider': - specifier: workspace:56.0.2 + specifier: workspace:^56.0.2 version: link:../@expo/local-build-cache-provider '@expo/log-box': - specifier: workspace:56.0.5 + specifier: workspace:^56.0.5 version: link:../@expo/log-box '@expo/metro': specifier: ~56.0.0 version: 56.0.0 '@expo/metro-config': - specifier: workspace:56.0.4 + specifier: workspace:~56.0.4 version: link:../@expo/metro-config '@expo/vector-icons': specifier: ^15.0.2 @@ -3482,10 +3482,10 @@ importers: specifier: workspace:~56.0.3 version: link:../expo-keep-awake expo-modules-autolinking: - specifier: workspace:56.0.2 + specifier: workspace:~56.0.2 version: link:../expo-modules-autolinking expo-modules-core: - specifier: workspace:56.0.4 + specifier: workspace:~56.0.4 version: link:../expo-modules-core pretty-format: specifier: ^29.7.0 @@ -3630,7 +3630,7 @@ importers: devDependencies: '@testing-library/react-native': specifier: ^13.3.0 - version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) + version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) '@types/node': specifier: ^22.14.0 version: 22.19.15 @@ -4089,13 +4089,13 @@ importers: packages/expo-dev-client: dependencies: expo-dev-launcher: - specifier: workspace:56.0.4 + specifier: workspace:~56.0.4 version: link:../expo-dev-launcher expo-dev-menu: - specifier: workspace:56.0.4 + specifier: workspace:~56.0.4 version: link:../expo-dev-menu expo-dev-menu-interface: - specifier: workspace:56.0.0 + specifier: workspace:~56.0.0 version: link:../expo-dev-menu-interface expo-manifests: specifier: workspace:~56.0.1 @@ -4123,7 +4123,7 @@ importers: specifier: workspace:^56.0.0 version: link:../@expo/schema-utils expo-dev-menu: - specifier: workspace:56.0.4 + specifier: workspace:~56.0.4 version: link:../expo-dev-menu expo-manifests: specifier: workspace:~56.0.1 @@ -4142,7 +4142,7 @@ importers: packages/expo-dev-menu: dependencies: expo-dev-menu-interface: - specifier: workspace:56.0.0 + specifier: workspace:~56.0.0 version: link:../expo-dev-menu-interface devDependencies: '@babel/preset-typescript': @@ -4150,7 +4150,7 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@testing-library/react-native': specifier: ^13.3.0 - version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) + version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) '@types/node': specifier: ^22.14.0 version: 22.19.15 @@ -4331,7 +4331,7 @@ importers: devDependencies: '@testing-library/react-native': specifier: ^13.3.0 - version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) + version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) '@types/fontfaceobserver': specifier: ^2.1.3 version: 2.1.3 @@ -5047,7 +5047,7 @@ importers: packages/expo-processing: dependencies: expo-gl: - specifier: workspace:56.0.3 + specifier: workspace:~56.0.3 version: link:../expo-gl processing-js: specifier: ^1.6.6 @@ -5060,7 +5060,7 @@ importers: packages/expo-router: dependencies: '@expo/log-box': - specifier: workspace:56.0.5 + specifier: workspace:^56.0.5 version: link:../@expo/log-box '@expo/metro-runtime': specifier: workspace:^56.0.4 @@ -5173,7 +5173,7 @@ importers: version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@testing-library/react-native': specifier: ^13.3.0 - version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) + version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) '@tsd/typescript': specifier: ^5.9.3 version: 5.9.3 @@ -5407,7 +5407,7 @@ importers: devDependencies: '@testing-library/react-native': specifier: ^13.3.0 - version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) + version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) '@types/better-sqlite3': specifier: ^7.6.6 version: 7.6.13 @@ -5459,7 +5459,7 @@ importers: version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@testing-library/react-native': specifier: ^13.3.0 - version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) + version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) '@types/node': specifier: ^22.14.0 version: 22.19.15 @@ -5654,10 +5654,10 @@ importers: version: link:../expo-module-scripts jest: specifier: ^29.3.1 - version: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) + version: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)) ts-jest: specifier: ^29.1.2 - version: 29.4.9(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(typescript@6.0.3) + version: 29.4.9(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)))(typescript@6.0.3) packages/expo-ui: dependencies: @@ -5682,7 +5682,7 @@ importers: version: 7.29.0 '@testing-library/react-native': specifier: ^13.3.0 - version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) + version: 13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3) '@types/babel__core': specifier: ^7.20.5 version: 7.20.5 @@ -18003,7 +18003,6 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - optional: true '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3))': dependencies: @@ -18877,9 +18876,7 @@ snapshots: metro-runtime: 0.84.4 transitivePeerDependencies: - '@babel/core' - - bufferutil - supports-color - - utf-8-validate '@react-native/normalize-colors@0.74.89': {} @@ -19184,18 +19181,6 @@ snapshots: optionalDependencies: jest: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)) - '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)': - dependencies: - jest-matcher-utils: 30.3.0 - picocolors: 1.1.1 - pretty-format: 30.3.0 - react: 19.2.3 - react-native: 0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3) - react-test-renderer: 19.2.3(react@19.2.3) - redent: 3.0.0 - optionalDependencies: - jest: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) - '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(react-native@0.85.3(@babel/core@7.29.0)(@react-native/jest-preset@0.85.3(@babel/core@7.29.0)(react@19.2.3))(@react-native/metro-config@0.85.3(@babel/core@7.29.0))(@types/react@19.2.14)(react@19.2.3))(react-test-renderer@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: jest-matcher-utils: 30.3.0 @@ -20910,22 +20895,6 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - optional: true - - create-jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node create-jest@29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)): dependencies: @@ -23174,26 +23143,6 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - optional: true - - jest-cli@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node jest-cli@29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)): dependencies: @@ -23244,7 +23193,6 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - supports-color - optional: true jest-config@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)): dependencies: @@ -23597,19 +23545,6 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - optional: true - - jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node jest@29.7.0(@types/node@25.5.0)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)): dependencies: @@ -26608,12 +26543,12 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.9(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)))(typescript@6.0.3): + ts-jest@29.4.9(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)))(typescript@6.0.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.9 - jest: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@25.5.0)(typescript@6.0.3)) + jest: 29.7.0(@types/node@22.19.15)(ts-node@10.9.2(@swc/core@1.15.18)(@types/node@22.19.15)(typescript@6.0.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6