From 605397a06be55c175e6a78e1eb911f5a99e60805 Mon Sep 17 00:00:00 2001 From: Christian Falch <875252+chrfalch@users.noreply.github.com> Date: Thu, 7 May 2026 12:58:51 +0200 Subject: [PATCH 01/24] fix(precomile) reduce output on CI (#45492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Why When building our precompiled XCFrameworks we get a lot of log output - a lot of it is not necessary. # How This PR fixes it by moving most of the trivial log output to a verbose option. This reduces the output when build from around 30.000 to 3000 lines with/without verbose. # Test-plan โœ… Ran both with/without `--verbose` option to verify that it still precompiles and displays warnings correctly. --- tools/src/Logger.ts | 22 ++++++ tools/src/commands/PrebuildPackages.ts | 6 +- tools/src/prebuilds/Dependencies.ts | 26 ++++--- tools/src/prebuilds/Frameworks.ts | 12 +-- tools/src/prebuilds/SPMBuild.ts | 16 ++-- tools/src/prebuilds/SPMGenerator.ts | 8 +- .../prebuilds/TransformReactXCFramework.ts | 20 ++--- tools/src/prebuilds/Utils.ts | 1 + tools/src/prebuilds/Verifier.ts | 4 +- tools/src/prebuilds/XCodeRunner.ts | 75 +++++++++++-------- tools/src/prebuilds/pipeline/RunSteps.ts | 12 ++- 11 files changed, 124 insertions(+), 78 deletions(-) diff --git a/tools/src/Logger.ts b/tools/src/Logger.ts index 4c63b52bb283b8..db3eb53a9591ef 100644 --- a/tools/src/Logger.ts +++ b/tools/src/Logger.ts @@ -9,13 +9,35 @@ const CONSOLE_RESOLVER: LoggerResolver = (level: LogLevel, color: Chalk | null, return console[level](...(color ? args.map((arg) => color(arg)) : args)); }; +// Process-wide verbose flag. All Logger instances (including LoggerBatch) +// share this โ€” verbosity is a property of the run, not of a logger object. +let _verbose = false; + /** * Basic logger just for simple console logging with colored output. */ export class Logger { constructor(readonly resolver: LoggerResolver = CONSOLE_RESOLVER) {} + /** + * Enables or disables verbose logging globally. When `false` (default), + * `logger.verbose(...)` calls are silently dropped. + */ + setVerbose(value: boolean): void { + _verbose = value; + } + + isVerbose(): boolean { + return _verbose; + } + + /** + * Emits a low-priority detail line. No-op unless `setVerbose(true)` has + * been called โ€” used for progress chatter that buries signal in CI logs + * (per-step "Cleaning ...", "Generating ...", subprocess line streams, etc). + */ verbose(...args: any[]): void { + if (!_verbose) return; this.resolver('debug', chalk.dim, args); } diff --git a/tools/src/commands/PrebuildPackages.ts b/tools/src/commands/PrebuildPackages.ts index e052a4271f5e8d..ea4c2bc70d2214 100644 --- a/tools/src/commands/PrebuildPackages.ts +++ b/tools/src/commands/PrebuildPackages.ts @@ -35,7 +35,11 @@ export default (program: Command) => { 'Generates `.xcframework` artifacts for iOS packages. If no package names are provided, discovers all packages with spm.config.json.' ) .alias('prebuild') - .option('-v, --verbose', 'Enable verbose output (full build logs instead of spinners).', false) + .option( + '-v, --verbose', + 'Print every build step and the full xcodebuild line stream. Off by default; in CI only success/failure lines, compile warnings/errors, and the run summary are printed.', + false + ) .option( '--react-native-version ', 'Provides the current React Native version. Auto-detected from bare-expo if not set.' diff --git a/tools/src/prebuilds/Dependencies.ts b/tools/src/prebuilds/Dependencies.ts index 53bbf1a3c205bc..124aed6f5a693c 100644 --- a/tools/src/prebuilds/Dependencies.ts +++ b/tools/src/prebuilds/Dependencies.ts @@ -90,7 +90,7 @@ export const Dependencies = { * @returns Whether downloading artifacts should be skipped. */ cleanArtifactsAsync: async (artifactsPath: string): Promise => { - logger.info( + logger.verbose( `๐Ÿงน Clearing artifacts folder: ${chalk.gray(path.relative(process.cwd(), artifactsPath))}` ); await fs.remove(artifactsPath); @@ -122,7 +122,7 @@ export const Dependencies = { react: `${currentVersions.reactNativeVersion}-${currentVersions.buildFlavor}`, }; - logger.info(`๐Ÿงน Pruning unused cache entries from ${chalk.gray(cachePath)}...`); + logger.verbose(`๐Ÿงน Pruning unused cache entries from ${chalk.gray(cachePath)}...`); // Iterate through artifact directories (hermes, react-native-dependencies, react) const artifactDirs = await fs.readdir(cachePath, { withFileTypes: true }); @@ -153,12 +153,12 @@ export const Dependencies = { if (versionDir.name === currentVersion) { // This is the current version - keep it keptCount++; - logger.info( + logger.verbose( ` โœ“ Keeping ${chalk.green(artifactDir.name)}/${chalk.cyan(versionDir.name)}` ); } else { // Old version - remove it - logger.info( + logger.verbose( ` ๐Ÿ—‘ Removing ${chalk.yellow(artifactDir.name)}/${chalk.gray(versionDir.name)}` ); await fs.remove(versionPath); @@ -168,9 +168,9 @@ export const Dependencies = { } if (removed.length > 0) { - logger.info(`๐Ÿงน Pruned ${chalk.yellow(removed.length)} old cache entries`); + logger.verbose(`๐Ÿงน Pruned ${chalk.yellow(removed.length)} old cache entries`); } else { - logger.info(`๐Ÿงน Cache is clean - no old entries to prune`); + logger.verbose(`๐Ÿงน Cache is clean - no old entries to prune`); } return { removed, keptCount }; @@ -196,7 +196,7 @@ export const Dependencies = { skipArtifacts, } = options; - logger.info( + logger.verbose( `โฌ‡๏ธ ${options.skipArtifacts ? 'Verifying' : 'Preparing'} centralized cache at ${chalk.gray(path.relative(process.cwd(), cachePath))}...` ); @@ -250,7 +250,7 @@ export const Dependencies = { const reactNativeSourcePath = resolvePackagePath('react-native'); const xcframeworkPath = path.join(reactNativePath, 'React.xcframework'); if (fs.existsSync(xcframeworkPath) && !isVFSGenerated(reactNativePath, reactNativeSourcePath)) { - logger.info('๐Ÿ”„ Generating VFS overlay for stock React.xcframework...'); + logger.verbose('๐Ÿ”„ Generating VFS overlay for stock React.xcframework...'); await transformReactXCFrameworkAsync({ outputPath: reactNativePath, reactNativePath: reactNativeSourcePath, @@ -287,7 +287,7 @@ export const Dependencies = { depsDestinationPath: string, copyDependencies: boolean ): Promise => { - logger.info( + logger.verbose( `๐Ÿ“‹ ${copyDependencies ? 'Syncing' : 'Checking'} package dependencies for ${chalk.green(pkg.packageName)}` ); @@ -349,7 +349,9 @@ export const Dependencies = { * @param pkg Package */ cleanDependenciesFolderAsync: async (pkg: SPMPackageSource): Promise => { - logger.info(`๐Ÿงน Cleaning dependencies folder for package ${chalk.green(pkg.packageName)}...`); + logger.verbose( + `๐Ÿงน Cleaning dependencies folder for package ${chalk.green(pkg.packageName)}...` + ); const buildFolderToClean = Dependencies.getPackageDependenciesPath(pkg); await fs.remove(buildFolderToClean); }, @@ -367,7 +369,7 @@ export const Dependencies = { const outputPath = path.join(pkg.buildPath, 'output', flavor.toLowerCase()); if (fs.existsSync(outputPath)) { - logger.info( + logger.verbose( `๐Ÿงน Cleaning output folder for package ${chalk.green(pkg.packageName)}/${flavor}...` ); await fs.remove(outputPath); @@ -382,7 +384,7 @@ export const Dependencies = { cleanGeneratedFolderAsync: async (pkg: SPMPackageSource): Promise => { const generatedPath = path.join(pkg.path, '.generated'); if (fs.existsSync(generatedPath)) { - logger.info(`๐Ÿงน Cleaning generated folder for package ${chalk.green(pkg.packageName)}...`); + logger.verbose(`๐Ÿงน Cleaning generated folder for package ${chalk.green(pkg.packageName)}...`); await fs.remove(generatedPath); } }, diff --git a/tools/src/prebuilds/Frameworks.ts b/tools/src/prebuilds/Frameworks.ts index e5ae5704296c29..b5bb44711e470f 100644 --- a/tools/src/prebuilds/Frameworks.ts +++ b/tools/src/prebuilds/Frameworks.ts @@ -41,7 +41,7 @@ const signXCFramework = ( identity: string, useTimestamp: boolean = true ): void => { - logger.info(`๐Ÿ” Signing XCFramework with identity "${identity}"...`); + logger.verbose(`๐Ÿ” Signing XCFramework with identity "${identity}"...`); const timestampFlag = useTimestamp ? '--timestamp' : ''; const command = `codesign ${timestampFlag} --sign "${identity}" "${xcframeworkPath}"`.trim(); @@ -74,7 +74,7 @@ export const Frameworks = { ): Promise => { const spmConfig = pkg.getSwiftPMConfiguration(); - logger.info( + logger.verbose( `๐Ÿงฉ Composing XCFramework for ${chalk.green(pkg.packageName) + '/' + chalk.green(product.name)}...` ); @@ -484,7 +484,7 @@ const copySPMDependencyXCFrameworksAsync = async ( const sharedPath = Frameworks.getSharedSPMDepFrameworkPath(productName, buildType); const destPath = path.join(outputDir, `${productName}.xcframework`); - logger.info( + logger.verbose( `๐Ÿ“ฆ Copying shared SPM dep ${chalk.cyan(productName)} from shared location โ†’ ${path.relative(pkg.path, destPath)}` ); await fs.remove(destPath); @@ -552,7 +552,7 @@ const copySPMDependencyXCFrameworksAsync = async ( const sourceXCFrameworkPath = path.join(artifactsDir, xcframeworkName); if (await fs.pathExists(sourceXCFrameworkPath)) { - logger.info( + logger.verbose( `๐Ÿ“ฆ Copying SPM dependency ${chalk.cyan(xcframeworkName)} โ†’ ${path.relative(pkg.path, destXCFrameworkPath)}` ); await fs.remove(destXCFrameworkPath); @@ -569,7 +569,7 @@ const copySPMDependencyXCFrameworksAsync = async ( } // Compose the dependency xcframework with only the relevant slices - logger.info( + logger.verbose( `๐Ÿ“ฆ Composing SPM dependency ${chalk.cyan(xcframeworkName)} โ†’ ${path.relative(pkg.path, destXCFrameworkPath)}` ); await fs.remove(destXCFrameworkPath); @@ -648,7 +648,7 @@ const createProductTarballAsync = async ( } } - logger.info( + logger.verbose( `๐Ÿ“ฆ Creating tarball for ${chalk.green(product.name)} (${buildType}): ${xcframeworkEntries.join(', ')}` ); diff --git a/tools/src/prebuilds/SPMBuild.ts b/tools/src/prebuilds/SPMBuild.ts index 1e20b95eb786fe..1dcb308278a407 100644 --- a/tools/src/prebuilds/SPMBuild.ts +++ b/tools/src/prebuilds/SPMBuild.ts @@ -34,7 +34,7 @@ export const SPMBuild = { platform?: BuildPlatform, hermesIncludeDirs?: string[] ): Promise => { - logger.info( + logger.verbose( `๐Ÿ— Build Package.swift for ${chalk.green(pkg.packageName)}/${chalk.green(product.name)} [${buildType.toLowerCase()}]` ); @@ -69,7 +69,7 @@ export const SPMBuild = { ); } - logger.info(`๐Ÿ— Swift package successfully built.`); + logger.verbose(`๐Ÿ— Swift package successfully built.`); }, /** @@ -84,7 +84,7 @@ export const SPMBuild = { buildType: BuildFlavor ): Promise => { const buildFolderToClean = SPMBuild.getPackageBuildPath(pkg, product, buildType); - logger.info( + logger.verbose( `๐Ÿงน Cleaning build folder ${chalk.green(path.relative(pkg.buildPath, buildFolderToClean))}...` ); await fs.remove(buildFolderToClean); @@ -414,7 +414,7 @@ async function resolveSPMDependenciesAndPatch(packageDir: string): Promise 'public typealias NetworkUnreachable = (_ReachabilityRef) -> ()' ); fs.writeFileSync(reachabilitySource, content, 'utf8'); - logger.info('๐Ÿฉน Patched Reachability.swift (library evolution typealias fix)'); + logger.verbose('๐Ÿฉน Patched Reachability.swift (library evolution typealias fix)'); } } } @@ -854,7 +854,7 @@ export async function buildSharedSPMDependencyAsync( return; } - logger.info( + logger.verbose( `๐Ÿ”จ Building shared SPM dependency ${chalk.cyan(productName)} [${buildType.toLowerCase()}]...` ); @@ -890,7 +890,7 @@ export async function buildSharedSPMDependencyAsync( // Package.swift changed or first run โ€” resolve from scratch await resolveSPMDependenciesAndPatch(buildDir); } else { - logger.info(` โญ๏ธ SPM dependencies already resolved (Package.swift unchanged)`); + logger.verbose(` โญ๏ธ SPM dependencies already resolved (Package.swift unchanged)`); } // Build from the dependency's own checkout so we get a framework with the correct @@ -973,7 +973,7 @@ export async function buildSharedSPMDependencyAsync( const externalInterDeps = interDeps.filter((d) => d !== productName); if (externalInterDeps.length > 0) { - logger.info( + logger.verbose( ` ๐Ÿ”— ${productName} depends on shared deps: ${externalInterDeps.map((d) => chalk.cyan(d)).join(', ')}` ); @@ -981,7 +981,7 @@ export async function buildSharedSPMDependencyAsync( for (const interDepName of externalInterDeps) { const interDep = allSharedDeps.get(interDepName); if (interDep && !Frameworks.hasSharedSPMDepFramework(interDepName, buildType)) { - logger.info(` โ†ณ Building dependency ${chalk.cyan(interDepName)} first...`); + logger.verbose(` โ†ณ Building dependency ${chalk.cyan(interDepName)} first...`); await buildSharedSPMDependencyAsync(interDep, buildType, allSharedDeps, platforms); } } diff --git a/tools/src/prebuilds/SPMGenerator.ts b/tools/src/prebuilds/SPMGenerator.ts index bf9c8e10d4ffff..fdb56a01f06b56 100644 --- a/tools/src/prebuilds/SPMGenerator.ts +++ b/tools/src/prebuilds/SPMGenerator.ts @@ -3,13 +3,13 @@ import fs from 'fs-extra'; import { glob } from 'glob'; import path from 'path'; +import logger from '../Logger'; import type { DownloadedDependencies } from './Artifacts.types'; import type { SPMPackageSource } from './ExternalPackage'; import { BuildFlavor } from './Prebuilder.types'; import { SPMProduct, SPMTarget } from './SPMConfig.types'; import { SPMPackage } from './SPMPackage'; import { createAsyncSpinner, hasFileContentChanged } from './Utils'; -import logger from '../Logger'; /** * Writes content to file only if it differs from existing content. @@ -42,7 +42,7 @@ export const SPMGenerator = { buildType: BuildFlavor, artifacts?: DownloadedDependencies ): Promise => { - logger.info( + logger.verbose( `๐Ÿ“ฆ Generating Package.swift for ${chalk.green(pkg.packageName)}/${chalk.green(product.name)}...` ); // Use SPMPackage to generate Package.swift @@ -73,7 +73,7 @@ export const SPMGenerator = { */ generateIsolatedSourcesForTargetsAsync: async (pkg: SPMPackageSource, product: SPMProduct) => { const loggerTitle = `${chalk.green(pkg.packageName)}/${chalk.green(product.name)}`; - logger.info(`๐Ÿ“‚ Generating files for ${loggerTitle}`); + logger.verbose(`๐Ÿ“‚ Generating files for ${loggerTitle}`); // Walk through all targets for the product and collect source files to copy into spm code structure for (const target of product.targets) { @@ -357,7 +357,7 @@ ${allImports.join('\n')} pkg: SPMPackageSource, product: SPMProduct ): Promise => { - logger.info( + logger.verbose( `๐Ÿงน Cleaning generated source code for ${chalk.green(pkg.packageName)}/${chalk.green(product.name)}...` ); diff --git a/tools/src/prebuilds/TransformReactXCFramework.ts b/tools/src/prebuilds/TransformReactXCFramework.ts index f4941175a07336..396743091e3f56 100644 --- a/tools/src/prebuilds/TransformReactXCFramework.ts +++ b/tools/src/prebuilds/TransformReactXCFramework.ts @@ -52,7 +52,7 @@ export async function transformReactXCFrameworkAsync(options: TransformOptions): // When present, use it directly instead of generating our own. const bundledTemplatePath = path.join(xcframeworkPath, 'React-VFS-template.yaml'); if (fs.existsSync(bundledTemplatePath)) { - logger.info(' Using bundled React-VFS-template.yaml from xcframework (RN 0.85+)'); + logger.verbose(' Using bundled React-VFS-template.yaml from xcframework (RN 0.85+)'); // Copy bundled template to expected location (where resolveVFSOverlayTemplate reads it) fs.copyFileSync(bundledTemplatePath, path.join(outputPath, 'React-VFS-template.yaml')); @@ -61,7 +61,7 @@ export async function transformReactXCFrameworkAsync(options: TransformOptions): const stagingDir = path.join(outputPath, 'React-extra-headers'); if (fs.existsSync(stagingDir)) { fs.removeSync(stagingDir); - logger.info(' Removed stale React-extra-headers/ directory'); + logger.verbose(' Removed stale React-extra-headers/ directory'); } // Write version stamp for cache invalidation @@ -70,23 +70,23 @@ export async function transformReactXCFrameworkAsync(options: TransformOptions): ).version; VersionStamp.write(outputPath, { reactNativeVersion: rnVersion }, VFS_STAMP_FILENAME); - logger.info(' VFS overlay setup complete (using bundled template).'); + logger.verbose(' VFS overlay setup complete (using bundled template).'); return; } - logger.info(' Collecting header mappings from podspecs...'); + logger.verbose(' Collecting header mappings from podspecs...'); const headerMappings = getHeaderFilesFromPodspecs(reactNativePath); - logger.info(' Inventorying stock xcframework headers...'); + logger.verbose(' Inventorying stock xcframework headers...'); const stockHeaders = inventoryStockHeaders(xcframeworkPath); - logger.info(' Detecting duplicate header basenames...'); + logger.verbose(' Detecting duplicate header basenames...'); const duplicateBasenames = findDuplicateBasenames(headerMappings); - logger.info(' Staging missing headers to React-extra-headers/...'); + logger.verbose(' Staging missing headers to React-extra-headers/...'); await stageMissingHeadersAsync(outputPath, headerMappings, stockHeaders, duplicateBasenames); - logger.info(' Generating VFS overlay template...'); + logger.verbose(' Generating VFS overlay template...'); const vfsYaml = createVFSOverlay(reactNativePath, stockHeaders, duplicateBasenames); fs.writeFileSync(path.join(outputPath, 'React-VFS-template.yaml'), vfsYaml); @@ -96,7 +96,7 @@ export async function transformReactXCFrameworkAsync(options: TransformOptions): ).version; VersionStamp.write(outputPath, { reactNativeVersion: rnVersion }, VFS_STAMP_FILENAME); - logger.info(' VFS overlay generation complete (xcframework untouched).'); + logger.verbose(' VFS overlay generation complete (xcframework untouched).'); } /** @@ -214,7 +214,7 @@ async function stageMissingHeadersAsync( } } - logger.info(` Staged ${stagedCount} missing headers to React-extra-headers/`); + logger.verbose(` Staged ${stagedCount} missing headers to React-extra-headers/`); } const VFS_STAMP_FILENAME = '.vfs-version-stamp'; diff --git a/tools/src/prebuilds/Utils.ts b/tools/src/prebuilds/Utils.ts index 36389eb76d623b..6a169451203ca9 100644 --- a/tools/src/prebuilds/Utils.ts +++ b/tools/src/prebuilds/Utils.ts @@ -546,6 +546,7 @@ export const createAsyncSpinner = ( logger.log(`${Prefix} ${chalk.yellow('โš ')} ${effectivePrefix(chalk.yellow)}${text ?? ''}`); }, info: (text?: string) => { + if (!logger.isVerbose()) return; logger.log(`${Prefix} ${chalk.blue('โ„น')} ${effectivePrefix(chalk.green)}${text ?? ''}`); }, }; diff --git a/tools/src/prebuilds/Verifier.ts b/tools/src/prebuilds/Verifier.ts index f80cac529ca97f..44e526df22e51f 100644 --- a/tools/src/prebuilds/Verifier.ts +++ b/tools/src/prebuilds/Verifier.ts @@ -35,7 +35,7 @@ export const FrameworkVerifier = { buildFlavor: BuildFlavor, options?: XCFrameworkVerifyOptions ): Promise> => { - logger.info( + logger.verbose( `๐Ÿ” Verifying xcframework for ${chalk.green(pkg.packageName)}/${chalk.green(product.name)} [${buildFlavor.toLowerCase()}]` ); @@ -246,7 +246,7 @@ export const FrameworkVerifier = { ` ${chalk.red('โœ—')} Resource bundle ${chalk.cyan(bundleName)} missing from slices: ${missingSlices.join(', ')}` ); } else { - logger.info( + logger.verbose( ` ${chalk.green('โœ“')} Resource bundle ${chalk.cyan(bundleName)} present in all slices (${foundSlices.join(', ')})` ); } diff --git a/tools/src/prebuilds/XCodeRunner.ts b/tools/src/prebuilds/XCodeRunner.ts index b144eb24f7da6b..56e454d73c7fa0 100644 --- a/tools/src/prebuilds/XCodeRunner.ts +++ b/tools/src/prebuilds/XCodeRunner.ts @@ -75,44 +75,18 @@ export async function spawnXcodeBuildWithSpinner( const statusText = `${spinnerText} ${summary.trim()}${warningText}`; if (hasWarnings) { spinner.warn(statusText); + // Surface the actual warnings even on success โ€” without this, CI + // logs only see the count and have no way to triage what changed. + printDiagnostics(formatter.warnings, []); } else { spinner.succeed(statusText); } } else { spinner.fail(`${spinnerText} failed with code ${code}`); summary && logger.error('\n' + summary.trim() + '\n'); - - // On failure, show warnings first (consistent with FrameworkVerifier) - if (hasWarnings) { - logger.log(chalk.gray(' Warnings:')); - formatter.warnings - .slice(0, 10) - .forEach((warn) => logger.log(chalk.gray(` ${warn}`))); - if (warningCount > 10) { - logger.log(chalk.gray(` ... and ${warningCount - 10} more warnings`)); - } - } - - // Show errors (consistent with FrameworkVerifier style) - if (formatter.errors.length > 0) { - logger.log(chalk.gray(' Errors:')); - formatter.errors - .slice(0, 10) - .forEach((err) => logger.log(chalk.gray(` ${err.trim()}`))); - if (formatter.errors.length > 10) { - logger.log(chalk.gray(` ... and ${formatter.errors.length - 10} more errors`)); - } - } - - // If formatter didn't capture any errors, fall back to showing raw output + printDiagnostics(formatter.warnings, formatter.errors); if (formatter.errors.length === 0) { - const rawErrors = extractRawCompileErrors(results); - if (rawErrors.length > 0) { - logger.log(chalk.gray(' Raw errors:')); - rawErrors.slice(0, 10).forEach((err) => logger.log(chalk.gray(` ${err}`))); - } else if (error) { - logger.log(chalk.gray(error)); - } + printRawErrorFallback(results, error); } } @@ -121,6 +95,45 @@ export async function spawnXcodeBuildWithSpinner( }); } +/** + * Print formatted compile warnings and errors. Used on both success-with- + * warnings and failure, so CI always surfaces diagnostics regardless of + * overall build status. + */ +function printDiagnostics(warnings: string[], errors: string[]): void { + const MAX = 10; + + if (warnings.length > 0) { + logger.log(chalk.gray(' Warnings:')); + warnings.slice(0, MAX).forEach((warn) => logger.log(chalk.gray(` ${warn}`))); + if (warnings.length > MAX) { + logger.log(chalk.gray(` ... and ${warnings.length - MAX} more warnings`)); + } + } + + if (errors.length > 0) { + logger.log(chalk.gray(' Errors:')); + errors.slice(0, MAX).forEach((err) => logger.log(chalk.gray(` ${err.trim()}`))); + if (errors.length > MAX) { + logger.log(chalk.gray(` ... and ${errors.length - MAX} more errors`)); + } + } +} + +/** + * Print fallback raw errors when the xcodebuild failed but the formatter + * didn't capture structured errors. Called only on failure. + */ +function printRawErrorFallback(rawOutput: string, stderr: string): void { + const rawErrors = extractRawCompileErrors(rawOutput); + if (rawErrors.length > 0) { + logger.log(chalk.gray(' Raw errors:')); + rawErrors.slice(0, 10).forEach((err) => logger.log(chalk.gray(` ${err}`))); + } else if (stderr) { + logger.log(chalk.gray(stderr)); + } +} + /** * Extract raw compile errors from xcodebuild output. * This is a fallback for when xcpretty's formatter doesn't capture errors diff --git a/tools/src/prebuilds/pipeline/RunSteps.ts b/tools/src/prebuilds/pipeline/RunSteps.ts index 29e03a9b7fed7f..2d30e5e714eeae 100644 --- a/tools/src/prebuilds/pipeline/RunSteps.ts +++ b/tools/src/prebuilds/pipeline/RunSteps.ts @@ -382,10 +382,14 @@ export const prepareInputsStep: Step = { async run(ctx) { const { request } = ctx; - // Enable verbose output (full build logs instead of spinners). - // Also force non-interactive when building in parallel โ€” ora spinners - // from concurrent packages would overwrite each other's terminal lines. - if (request.verbose || request.concurrency > 1) { + // Verbose widens what gets logged but doesn't change interactive vs. + // non-interactive โ€” TTY users can opt into the full per-step trace and + // still see spinners. CI is non-interactive on its own. + logger.setVerbose(request.verbose); + + // Force non-interactive when building in parallel โ€” ora spinners from + // concurrent packages would overwrite each other's terminal lines. + if (request.concurrency > 1) { setForceNonInteractive(true); } From c311fb9abae18212f56e865cc6bcbba3ad3f1802 Mon Sep 17 00:00:00 2001 From: Christian Falch <875252+chrfalch@users.noreply.github.com> Date: Thu, 7 May 2026 12:59:14 +0200 Subject: [PATCH 02/24] fix(precompile) fixed react-native-skia prebuild failing (#45483) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Why When building 3rd party XCFrameworks, Skia failed locally when built without cleaning: ``` โŒ cpp/api/CustomBlendModes.h:10:15 > 10 | constexpr int kBlendModePlusDarker = 1001; | ^ redefinition of 'kBlendModePlusDarker' ... redefinition of 'kBlendModePlusLighter', 'kPlusDarkerSkSL', 'kPlusLighterSkSL', 'CustomBlenders', 'applyBlendMode' โŒ Build Swift package failed: xcodebuild failed with code 65 ``` This was caused by a stale hardlink after pnpm reinstall. # How SPMGenerator.ts flattens all package headers into a staging dir using hardlinks, specifically so #pragma once works across include paths (clang dedupes by inode). The skip condition for the relink, however, was hasFileContentChanged(...) โ€” content/size/mtime, not inode. When pnpm reinstalled Skia (different patch hash โ†’ different content-addressed .pnpm/ dir โ†’ fresh inode for CustomBlendModes.h, identical bytes), the staged hardlink kept its old inode. Within a single TU, clang then saw the header via two different inodes (staged vs. -I source path) and processed it twice โ†’ redefinition errors for every symbol in it. Confirmed empirically: source (May 6 20:30): inode 1761296556 staged (Apr 21 14:08): inode 1738862996 Fix: new exported helper refreshHardlinkIfNeeded(src, dest) that keys on ino + dev and unlinks/relinks when stale. Replaces the inline content-comparison block. Test in SPMGenerator.hardlinkRefresh.test.ts simulates the same-content-new-inode case. When running `et prebuild @shopify/react-native-skia` a small bug after fix to ExpoWidgets build (#45461) the `@` prefix fell through. A fix/test for this was also added by making `pathToLocalPackageJson` keep the `require.resolve` test so that 3rd party projects use the old path.join logic. # Test-plan โœ… Run `et prebuild @shopify/react-native-skia` and verify that it works without failure. --- tools/src/Packages.test.ts | 5 ++ tools/src/Packages.ts | 8 ++- .../SPMGenerator.hardlinkRefresh.test.ts | 52 +++++++++++++++++++ tools/src/prebuilds/SPMGenerator.ts | 34 +++++++++--- 4 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 tools/src/prebuilds/SPMGenerator.hardlinkRefresh.test.ts diff --git a/tools/src/Packages.test.ts b/tools/src/Packages.test.ts index d8138b841b806e..2b8025339ca61a 100644 --- a/tools/src/Packages.test.ts +++ b/tools/src/Packages.test.ts @@ -27,4 +27,9 @@ describe('getPackageByName', () => { it('returns null for an unknown package name', () => { assert.equal(getPackageByName('definitely-not-a-real-package'), null); }); + + it('returns null for third-party scoped packages installed only under node_modules', () => { + // @babel/core is reachable via the node_modules walk-up but is not a workspace package. + assert.equal(getPackageByName('@babel/core'), null); + }); }); diff --git a/tools/src/Packages.ts b/tools/src/Packages.ts index 4b54f982a9bf1f..007d31e1d104b1 100644 --- a/tools/src/Packages.ts +++ b/tools/src/Packages.ts @@ -487,7 +487,13 @@ function readExpoModuleConfigJson(expoModuleConfigJsonPath: string) { function pathToLocalPackageJson(packageName: string): string { if (packageName.startsWith('@')) { try { - return require.resolve(`${packageName}/package.json`, { paths: [PACKAGES_DIR] }); + const resolved = require.resolve(`${packageName}/package.json`, { paths: [PACKAGES_DIR] }); + // require.resolve walks up node_modules/. Reject realpaths outside PACKAGES_DIR + // so third-party scoped installs (e.g. @babel/core) fall through to the cache lookup. + const rel = path.relative(PACKAGES_DIR, fs.realpathSync(resolved)); + if (!rel.startsWith('..') && !path.isAbsolute(rel)) { + return resolved; + } } catch { // Fall through โ€” caller's cachedPackages fallback still applies. } diff --git a/tools/src/prebuilds/SPMGenerator.hardlinkRefresh.test.ts b/tools/src/prebuilds/SPMGenerator.hardlinkRefresh.test.ts new file mode 100644 index 00000000000000..eea3e0fb35dc7f --- /dev/null +++ b/tools/src/prebuilds/SPMGenerator.hardlinkRefresh.test.ts @@ -0,0 +1,52 @@ +import fs from 'fs-extra'; +import assert from 'node:assert/strict'; +import os from 'node:os'; +import { describe, it } from 'node:test'; +import path from 'path'; + +import { refreshHardlinkIfNeeded } from './SPMGenerator'; + +describe('refreshHardlinkIfNeeded', () => { + it('relinks when the source inode changed even though content is identical', async () => { + const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'spm-hardlink-')); + try { + const src = path.join(tmp, 'src.h'); + const dest = path.join(tmp, 'staging', 'dest.h'); + const content = '#pragma once\nint X = 1;\n'; + await fs.outputFile(src, content); + + assert.equal(await refreshHardlinkIfNeeded(src, dest), true); + const inodeBefore = (await fs.stat(dest)).ino; + assert.equal(inodeBefore, (await fs.stat(src)).ino); + + // Simulate pnpm reinstall: same bytes, fresh inode. + await fs.remove(src); + await fs.outputFile(src, content); + const newSrcInode = (await fs.stat(src)).ino; + assert.notEqual(newSrcInode, inodeBefore); + assert.equal((await fs.stat(dest)).ino, inodeBefore); + + assert.equal(await refreshHardlinkIfNeeded(src, dest), true); + assert.equal((await fs.stat(dest)).ino, newSrcInode); + } finally { + await fs.remove(tmp); + } + }); + + it('is a no-op when source and dest already share an inode', async () => { + const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'spm-hardlink-')); + try { + const src = path.join(tmp, 'src.h'); + const dest = path.join(tmp, 'staging', 'dest.h'); + await fs.outputFile(src, 'x'); + + assert.equal(await refreshHardlinkIfNeeded(src, dest), true); + const ctimeAfterFirst = (await fs.stat(dest)).ctimeMs; + + assert.equal(await refreshHardlinkIfNeeded(src, dest), false); + assert.equal((await fs.stat(dest)).ctimeMs, ctimeAfterFirst); + } finally { + await fs.remove(tmp); + } + }); +}); diff --git a/tools/src/prebuilds/SPMGenerator.ts b/tools/src/prebuilds/SPMGenerator.ts index fdb56a01f06b56..45d85775228255 100644 --- a/tools/src/prebuilds/SPMGenerator.ts +++ b/tools/src/prebuilds/SPMGenerator.ts @@ -11,6 +11,31 @@ import { SPMProduct, SPMTarget } from './SPMConfig.types'; import { SPMPackage } from './SPMPackage'; import { createAsyncSpinner, hasFileContentChanged } from './Utils'; +/** + * Ensures `destPath` is a hardlink to `sourcePath`. For staged headers, inode equality + * is the source of truth โ€” clang's `#pragma once` keys on inode, so a stale dest with + * identical bytes but a different inode is processed as a separate header within the + * same TU and produces redefinition errors. This is reachable when the package manager + * rewrites the source file with the same content but a fresh inode (pnpm reinstalls + * into a new content-addressed `.pnpm/` dir on patch-hash or version changes). + */ +export async function refreshHardlinkIfNeeded( + sourcePath: string, + destPath: string +): Promise { + const srcStat = await fs.stat(sourcePath); + const destStat = await fs.lstat(destPath).catch(() => null); + if (destStat && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) { + return false; + } + if (destStat) { + await fs.remove(destPath); + } + await fs.ensureDir(path.dirname(destPath)); + await fs.link(sourcePath, destPath); + return true; +} + /** * Writes content to file only if it differs from existing content. * Preserves mtime for Xcode incremental builds. @@ -243,16 +268,11 @@ export const SPMGenerator = { path.basename(file) ); - await fs.ensureDir(path.dirname(destinationFilePath)); - if (hasFileContentChanged(sourceFilePath, destinationFilePath)) { + const linked = await refreshHardlinkIfNeeded(sourceFilePath, destinationFilePath); + if (linked) { spinner.info( `Linking header file for target ${chalk.green(target.name)} ${path.basename(file)}...` ); - // Remove existing file before creating hardlink - if (await fs.pathExists(destinationFilePath)) { - await fs.remove(destinationFilePath); - } - await fs.link(sourceFilePath, destinationFilePath); } } } From bea363bef089295e2c2c08dd542b073ea80c8393 Mon Sep 17 00:00:00 2001 From: Evan Bacon Date: Thu, 7 May 2026 04:09:49 -0700 Subject: [PATCH 03/24] chore(cli): Bump expo/xcpretty (#45473) # Why - add support for formatting failed script execution in xcodebuild. # How # Test Plan # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --------- Co-authored-by: Phil Pluckthun Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com> --- packages/@expo/cli/CHANGELOG.md | 2 ++ packages/@expo/cli/package.json | 2 +- pnpm-lock.yaml | 14 +++++++------- tools/package.json | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/@expo/cli/CHANGELOG.md b/packages/@expo/cli/CHANGELOG.md index 68c6fc942a6469..83e5a0f7c14a6c 100644 --- a/packages/@expo/cli/CHANGELOG.md +++ b/packages/@expo/cli/CHANGELOG.md @@ -10,6 +10,8 @@ ### ๐Ÿ’ก Others +- Bump to `@expo/xcpretty@^4.4.4` ([#45473](https://github.com/expo/expo/pull/45473) by [@EvanBacon](https://github.com/EvanBacon)) + ## 56.0.5 โ€” 2026-05-06 _This version does not introduce any user-facing changes._ diff --git a/packages/@expo/cli/package.json b/packages/@expo/cli/package.json index 526b9c67357b6f..83fbc33d870e9b 100644 --- a/packages/@expo/cli/package.json +++ b/packages/@expo/cli/package.json @@ -70,7 +70,7 @@ "@expo/schema-utils": "workspace:^56.0.0", "@expo/spawn-async": "^1.7.2", "@expo/ws-tunnel": "^1.0.1", - "@expo/xcpretty": "^4.4.0", + "@expo/xcpretty": "^4.4.4", "@react-native/dev-middleware": "0.85.3", "accepts": "^1.3.8", "arg": "^5.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4884ebaae2499..8ef0e5592cf6cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1837,8 +1837,8 @@ importers: specifier: ^1.0.1 version: 1.0.6 '@expo/xcpretty': - specifier: ^4.4.0 - version: 4.4.1 + specifier: ^4.4.4 + version: 4.4.4 '@react-native/dev-middleware': specifier: 0.85.3 version: 0.85.3(patch_hash=563dd3c12ac9064f72bebd975abb206061af92d430d10341a2fce54810b5128f) @@ -6131,8 +6131,8 @@ importers: specifier: ^0.57.1 version: 0.57.1 '@expo/xcpretty': - specifier: ^4.4.0 - version: 4.4.1 + specifier: ^4.4.4 + version: 4.4.4 '@linear/sdk': specifier: ^2.6.0 version: 2.6.0(encoding@0.1.13) @@ -7577,8 +7577,8 @@ packages: '@expo/ws-tunnel@1.0.6': resolution: {integrity: sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==} - '@expo/xcpretty@4.4.1': - resolution: {integrity: sha512-KZNxZvnGCtiM2aYYZ6Wz0Ix5r47dAvpNLApFtZWnSoERzAdOMzVBOPysBoM0JlF6FKWZ8GPqgn6qt3dV/8Zlpg==} + '@expo/xcpretty@4.4.4': + resolution: {integrity: sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==} hasBin: true '@firebase/analytics-compat@0.2.6': @@ -17429,7 +17429,7 @@ snapshots: '@expo/ws-tunnel@1.0.6': {} - '@expo/xcpretty@4.4.1': + '@expo/xcpretty@4.4.4': dependencies: '@babel/code-frame': 7.29.0 chalk: 4.1.2 diff --git a/tools/package.json b/tools/package.json index 5011e4f52d5ad1..d8cb0b5964dfdc 100644 --- a/tools/package.json +++ b/tools/package.json @@ -35,7 +35,7 @@ "@expo/multipart-body-parser": "^1.1.0", "@expo/osascript": "workspace:*", "@expo/plist": "workspace:*", - "@expo/xcpretty": "^4.4.0", + "@expo/xcpretty": "^4.4.4", "@expo/spawn-async": "^1.7.2", "@expo/swiftlint": "^0.57.1", "@linear/sdk": "^2.6.0", From 10ac0b52e7471a143f2a113e5444b60bd274f00d Mon Sep 17 00:00:00 2001 From: Mathieu Acthernoene Date: Thu, 7 May 2026 13:09:52 +0200 Subject: [PATCH 04/24] Add zoontek to codemention (#45486) --- .github/codemention.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/codemention.yml b/.github/codemention.yml index 5b9557cc94cae2..752daa9e7ca458 100644 --- a/.github/codemention.yml +++ b/.github/codemention.yml @@ -139,7 +139,7 @@ rules: - patterns: ['packages/expo-local-authentication/**'] mentions: ['behenate'] - patterns: ['packages/expo-localization/**'] - mentions: ['EvanBacon'] + mentions: ['EvanBacon', 'zoontek'] - patterns: ['packages/expo-location/**'] mentions: ['behenate', 'lukmccall'] - patterns: ['packages/expo-mail-composer/**'] @@ -165,7 +165,7 @@ rules: - patterns: ['packages/expo-modules-test-core/**'] mentions: ['lukmccall', 'tsapeta'] - patterns: ['packages/expo-navigation-bar/**'] - mentions: ['EvanBacon'] + mentions: ['EvanBacon', 'zoontek'] - patterns: ['packages/expo-network/**'] mentions: ['ide', 'lukmccall'] - patterns: ['packages/expo-network-addons/**'] @@ -195,13 +195,13 @@ rules: - patterns: ['packages/expo-speech/**'] mentions: ['lukmccall', 'alanjhughes'] - patterns: ['packages/expo-splash-screen/**'] - mentions: ['alanjhughes'] + mentions: ['alanjhughes', 'zoontek'] - patterns: ['packages/expo-sqlite/**'] mentions: ['Kudo', 'alanjhughes'] - patterns: ['packages/expo-standard-web-crypto/**'] mentions: ['EvanBacon'] - patterns: ['packages/expo-status-bar/**'] - mentions: ['brentvatne'] + mentions: ['brentvatne', 'zoontek'] - patterns: ['packages/expo-store-review/**'] mentions: ['EvanBacon'] - patterns: ['packages/expo-structured-headers/**'] From 88b8e0163ceebe1a79271c4634aa45a072ff3698 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Thu, 7 May 2026 12:10:04 +0100 Subject: [PATCH 05/24] test(e2e): Fix manifest version fixture (#45470) # Why # How # Test Plan # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/@expo/cli/e2e/__tests__/start-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@expo/cli/e2e/__tests__/start-test.ts b/packages/@expo/cli/e2e/__tests__/start-test.ts index 776042c74e04fb..5da4132024d1c0 100644 --- a/packages/@expo/cli/e2e/__tests__/start-test.ts +++ b/packages/@expo/cli/e2e/__tests__/start-test.ts @@ -132,7 +132,7 @@ describeSkipWin('server', () => { // Manifest expect(manifest.runtimeVersion).toBe('1.0'); - expect(manifest.extra.expoClient?.sdkVersion).toBe('55.0.0'); + expect(manifest.extra.expoClient?.sdkVersion).toMatch(/\d+\.0\.0/); expect(manifest.extra.expoClient?.slug).toBe('basic-start'); expect(manifest.extra.expoClient?.name).toBe('basic-start'); From f1e7237fd8aa329d89d09c133d3bebe285d17c02 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Thu, 7 May 2026 12:10:40 +0100 Subject: [PATCH 06/24] fix(expo-router): Move `@jest/globals` to `devDependencies` (#45469) # Why Unless I'm missing something, this was probably accidental from the pnpm migration. # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/expo-router/CHANGELOG.md | 2 ++ packages/expo-router/package.json | 2 +- pnpm-lock.yaml | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/expo-router/CHANGELOG.md b/packages/expo-router/CHANGELOG.md index 1d6c542a41caeb..727fabd3aea34f 100644 --- a/packages/expo-router/CHANGELOG.md +++ b/packages/expo-router/CHANGELOG.md @@ -10,6 +10,8 @@ ### ๐Ÿ’ก Others +- Move `@jest/globals` to `devDependencies` ([#45469](https://github.com/expo/expo/pull/45469) by [@kitten](https://github.com/kitten)) + ## 56.0.4 โ€” 2026-05-06 _This version does not introduce any user-facing changes._ diff --git a/packages/expo-router/package.json b/packages/expo-router/package.json index 78ce4cd2875ec0..23a9d5510912cd 100644 --- a/packages/expo-router/package.json +++ b/packages/expo-router/package.json @@ -125,6 +125,7 @@ } }, "devDependencies": { + "@jest/globals": "^29.7.0", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.3.0", "@testing-library/react-native": "^13.3.0", @@ -151,7 +152,6 @@ "@expo/metro-runtime": "workspace:^56.0.4", "@expo/schema-utils": "workspace:^56.0.0", "@expo/ui": "workspace:^56.0.1", - "@jest/globals": "^29.7.0", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-native-masked-view/masked-view": "^0.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ef0e5592cf6cb..a4141f5a3f8dc6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5068,9 +5068,6 @@ importers: '@expo/ui': specifier: workspace:^56.0.1 version: link:../expo-ui - '@jest/globals': - specifier: ^29.7.0 - version: 29.7.0 '@radix-ui/react-slot': specifier: ^1.2.0 version: 1.2.4(@types/react@19.2.14)(react@19.2.3) @@ -5162,6 +5159,9 @@ importers: specifier: ^1.1.2 version: 1.1.2(@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) devDependencies: + '@jest/globals': + specifier: ^29.7.0 + version: 29.7.0 '@testing-library/dom': specifier: ^10.4.0 version: 10.4.1 From 683f2e185379051df1927ec41d381b17b57def07 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Thu, 7 May 2026 12:12:46 +0100 Subject: [PATCH 07/24] feat(create-expo): Drop automatic `node-linker=hoisted` for pnpm (#45491) # Why Bun is already defaulting to the isolated install, and since we support this well now (and have for a while), and are using pnpm ourselves now, we should drop this for newly created projects. # How - Drop setting `node-linker=hoisted` when creating projects with pnpm **Note:** I didn't mark this as a feature or breaking. It feels natural to just put it in "Others", since it's for new projects (so technically not breaking), and isn't a feature but a removal. # Test Plan - Manually tested # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/create-expo/CHANGELOG.md | 2 ++ packages/create-expo/e2e/__tests__/index-test.ts | 6 ------ packages/create-expo/src/resolvePackageManager.ts | 4 ---- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/create-expo/CHANGELOG.md b/packages/create-expo/CHANGELOG.md index 7d55dea475f66f..9b3b39d6006ee1 100644 --- a/packages/create-expo/CHANGELOG.md +++ b/packages/create-expo/CHANGELOG.md @@ -10,6 +10,8 @@ ### ๐Ÿ’ก Others +- Drop automatically setting `node-linker=hoisted` for pnpm ([#45491](https://github.com/expo/expo/pull/45491) by [@kitten](https://github.com/kitten)) + ## 3.7.2 โ€” 2026-05-06 _This version does not introduce any user-facing changes._ diff --git a/packages/create-expo/e2e/__tests__/index-test.ts b/packages/create-expo/e2e/__tests__/index-test.ts index dc6e93ca0454a8..a4f1332520bea7 100644 --- a/packages/create-expo/e2e/__tests__/index-test.ts +++ b/packages/create-expo/e2e/__tests__/index-test.ts @@ -93,12 +93,6 @@ it('uses pnpm', async () => { expectFileExists(projectName, '.gitignore'); // Check if it skipped install expectFileNotExists(projectName, 'node_modules'); - - // Check if `pnpm` node linker is set - const { stdout } = expectExecutePassing( - await spawnAsync('pnpm', ['config', 'get', 'node-linker'], { cwd: getTestPath(projectName) }) - ); - expect(stdout).toContain('hoisted'); }); it('uses Bun', async () => { diff --git a/packages/create-expo/src/resolvePackageManager.ts b/packages/create-expo/src/resolvePackageManager.ts index df3105115dc915..cbb801d6720104 100644 --- a/packages/create-expo/src/resolvePackageManager.ts +++ b/packages/create-expo/src/resolvePackageManager.ts @@ -105,10 +105,6 @@ export async function configurePackageManager( ) { const manager = createPackageManager(packageManager, { cwd: projectRoot, ...flags }); switch (manager.name) { - case 'pnpm': - await manager.runAsync(['config', '--location', 'project', 'set', 'node-linker', 'hoisted']); - break; - case 'yarn': { const yarnVersion = await manager.versionAsync(); const majorVersion = parseInt(yarnVersion.split('.')[0] ?? '', 10); From 1980cb8133b2ee1c61c65109ff0598f8afe7712f Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Thu, 7 May 2026 12:14:47 +0100 Subject: [PATCH 08/24] chore(metro-config): Drop obsolete `EXPO_USE_EXOTIC` flag (#45494) # Why This has been obsolete for a while, and there seems to be no need to check for this anymore. # How - Drop `EXPO_USE_EXOTIC` flag # Test Plan - n/a # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/@expo/metro-config/CHANGELOG.md | 2 ++ packages/@expo/metro-config/build/ExpoMetroConfig.js | 6 ------ .../@expo/metro-config/build/ExpoMetroConfig.js.map | 2 +- packages/@expo/metro-config/build/env.d.ts | 2 -- packages/@expo/metro-config/build/env.js | 4 ---- packages/@expo/metro-config/build/env.js.map | 2 +- packages/@expo/metro-config/src/ExpoMetroConfig.ts | 12 ------------ packages/@expo/metro-config/src/env.ts | 5 ----- 8 files changed, 4 insertions(+), 31 deletions(-) diff --git a/packages/@expo/metro-config/CHANGELOG.md b/packages/@expo/metro-config/CHANGELOG.md index 82f30f0f4667d9..793461c7b8cb57 100644 --- a/packages/@expo/metro-config/CHANGELOG.md +++ b/packages/@expo/metro-config/CHANGELOG.md @@ -10,6 +10,8 @@ ### ๐Ÿ’ก Others +- Drop obsolete `EXPO_USE_EXOTIC` flag warning ([#45494](https://github.com/expo/expo/pull/45494) by [@kitten](https://github.com/kitten)) + ## 56.0.3 โ€” 2026-05-06 _This version does not introduce any user-facing changes._ diff --git a/packages/@expo/metro-config/build/ExpoMetroConfig.js b/packages/@expo/metro-config/build/ExpoMetroConfig.js index 51479f49c8f36d..1520b818c698f7 100644 --- a/packages/@expo/metro-config/build/ExpoMetroConfig.js +++ b/packages/@expo/metro-config/build/ExpoMetroConfig.js @@ -29,7 +29,6 @@ const filePath_1 = require("./utils/filePath"); const getPkgVersion_1 = require("./utils/getPkgVersion"); const setOnReadonly_1 = require("./utils/setOnReadonly"); const debug = require('debug')('expo:metro:config'); -let hasWarnedAboutExotic = false; let hasWarnedAboutReactNative = false; // Patch Metro's graph to support always parsing certain modules. This enables // things like Tailwind CSS which update based on their own heuristics. @@ -134,11 +133,6 @@ function getDefaultConfig(projectRoot, { mode, isCSSEnabled = true, unstable_bef if (isCSSEnabled) { patchMetroGraphToSupportUncachedModules(); } - const isExotic = mode === 'exotic' || env_1.env.EXPO_USE_EXOTIC; - if (isExotic && !hasWarnedAboutExotic) { - hasWarnedAboutExotic = true; - console.log(chalk_1.default.gray(`\u203A Feature ${chalk_1.default.bold `EXPO_USE_EXOTIC`} has been removed in favor of the default transformer.`)); - } const reactNativePath = path_1.default.dirname(resolve_from_1.default.silent(projectRoot, 'react-native/package.json') ?? 'react-native/package.json'); if (reactNativePath === 'react-native' && !hasWarnedAboutReactNative) { hasWarnedAboutReactNative = true; diff --git a/packages/@expo/metro-config/build/ExpoMetroConfig.js.map b/packages/@expo/metro-config/build/ExpoMetroConfig.js.map index a2bb4a8fab5354..3b116adc82754b 100644 --- a/packages/@expo/metro-config/build/ExpoMetroConfig.js.map +++ b/packages/@expo/metro-config/build/ExpoMetroConfig.js.map @@ -1 +1 @@ -{"version":3,"file":"ExpoMetroConfig.js","sourceRoot":"","sources":["../src/ExpoMetroConfig.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"ExpoMetroConfig.js","sourceRoot":"","sources":["../src/ExpoMetroConfig.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/@expo/metro-config/build/env.d.ts b/packages/@expo/metro-config/build/env.d.ts index 251ecef5cf4b5a..a346131090e404 100644 --- a/packages/@expo/metro-config/build/env.d.ts +++ b/packages/@expo/metro-config/build/env.d.ts @@ -1,8 +1,6 @@ declare class Env { /** Enable debug logging */ get EXPO_DEBUG(): boolean; - /** Enable the experimental "exotic" mode. [Learn more](https://blog.expo.dev/drastically-faster-bundling-in-react-native-a54f268e0ed1). */ - get EXPO_USE_EXOTIC(): boolean; /** The React Metro port that's baked into react-native scripts and tools. */ get RCT_METRO_PORT(): number; /** Disable Environment Variable injection in client bundles. */ diff --git a/packages/@expo/metro-config/build/env.js b/packages/@expo/metro-config/build/env.js index 88b736b2b920a2..f1f4d83d314a6a 100644 --- a/packages/@expo/metro-config/build/env.js +++ b/packages/@expo/metro-config/build/env.js @@ -7,10 +7,6 @@ class Env { get EXPO_DEBUG() { return (0, getenv_1.boolish)('EXPO_DEBUG', false); } - /** Enable the experimental "exotic" mode. [Learn more](https://blog.expo.dev/drastically-faster-bundling-in-react-native-a54f268e0ed1). */ - get EXPO_USE_EXOTIC() { - return (0, getenv_1.boolish)('EXPO_USE_EXOTIC', false); - } /** The React Metro port that's baked into react-native scripts and tools. */ get RCT_METRO_PORT() { return (0, getenv_1.int)('RCT_METRO_PORT', 8081); diff --git a/packages/@expo/metro-config/build/env.js.map b/packages/@expo/metro-config/build/env.js.map index a456d1366c20a1..a93a7c193125b9 100644 --- a/packages/@expo/metro-config/build/env.js.map +++ b/packages/@expo/metro-config/build/env.js.map @@ -1 +1 @@ -{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AAEtC,MAAM,GAAG;IACP,2BAA2B;IAC3B,IAAI,UAAU;QACZ,OAAO,IAAA,gBAAO,EAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,2IAA2I;IAC3I,IAAI,eAAe;QACjB,OAAO,IAAA,gBAAO,EAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,6EAA6E;IAC7E,IAAI,cAAc;QAChB,OAAO,IAAA,YAAG,EAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,gEAAgE;IAChE,IAAI,uBAAuB;QACzB,OAAO,IAAA,gBAAO,EAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;IACnD,CAAC;IAED,6JAA6J;IAC7J,IAAI,sBAAsB;QACxB,OAAO,IAAA,gBAAO,EAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;CACF;AAEY,QAAA,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AAEtC,MAAM,GAAG;IACP,2BAA2B;IAC3B,IAAI,UAAU;QACZ,OAAO,IAAA,gBAAO,EAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,6EAA6E;IAC7E,IAAI,cAAc;QAChB,OAAO,IAAA,YAAG,EAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,gEAAgE;IAChE,IAAI,uBAAuB;QACzB,OAAO,IAAA,gBAAO,EAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;IACnD,CAAC;IAED,6JAA6J;IAC7J,IAAI,sBAAsB;QACxB,OAAO,IAAA,gBAAO,EAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;CACF;AAEY,QAAA,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC"} \ No newline at end of file diff --git a/packages/@expo/metro-config/src/ExpoMetroConfig.ts b/packages/@expo/metro-config/src/ExpoMetroConfig.ts index 773eece4701bc3..fe027e468f5d34 100644 --- a/packages/@expo/metro-config/src/ExpoMetroConfig.ts +++ b/packages/@expo/metro-config/src/ExpoMetroConfig.ts @@ -56,7 +56,6 @@ export interface DefaultConfigOptions { }) => Module[])[]; } -let hasWarnedAboutExotic = false; let hasWarnedAboutReactNative = false; // Patch Metro's graph to support always parsing certain modules. This enables @@ -197,17 +196,6 @@ export function getDefaultConfig( patchMetroGraphToSupportUncachedModules(); } - const isExotic = mode === 'exotic' || env.EXPO_USE_EXOTIC; - - if (isExotic && !hasWarnedAboutExotic) { - hasWarnedAboutExotic = true; - console.log( - chalk.gray( - `\u203A Feature ${chalk.bold`EXPO_USE_EXOTIC`} has been removed in favor of the default transformer.` - ) - ); - } - const reactNativePath = path.dirname( resolveFrom.silent(projectRoot, 'react-native/package.json') ?? 'react-native/package.json' ); diff --git a/packages/@expo/metro-config/src/env.ts b/packages/@expo/metro-config/src/env.ts index e2c9dbef1e8bf2..f15c0752c226c6 100644 --- a/packages/@expo/metro-config/src/env.ts +++ b/packages/@expo/metro-config/src/env.ts @@ -6,11 +6,6 @@ class Env { return boolish('EXPO_DEBUG', false); } - /** Enable the experimental "exotic" mode. [Learn more](https://blog.expo.dev/drastically-faster-bundling-in-react-native-a54f268e0ed1). */ - get EXPO_USE_EXOTIC() { - return boolish('EXPO_USE_EXOTIC', false); - } - /** The React Metro port that's baked into react-native scripts and tools. */ get RCT_METRO_PORT() { return int('RCT_METRO_PORT', 8081); From b8b37cf63ca7c23a8cd2e1841a5847c9e6352bd6 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Thu, 7 May 2026 12:22:32 +0100 Subject: [PATCH 09/24] chore(cli): Drop obsolete webview check (#45489) # Why We shouldn't have to check for `@expo/dom-webview` anymore, since it's automatically installed (and hence linked) via `expo`. We have `react-native-webview` as a fallback, but no check is technically required here anymore. We might want to tweak the dependency structure, but, as it stands, the resolution here isn't correct for isolated dependencies, and likely always failed. Just dropping the check since we know that _a_ webview (`@expo/dom-webview`) will be installed is fine though. At worst, we're moving an error for edge case setups into the runtime. # How - Drop webview installation check # Test Plan **Note:** Admittedly haven't tested this manually, but I have seen this fail when `react-native-webview` isn't installed in isolated installations, which is caused by the invalid check. # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/@expo/cli/CHANGELOG.md | 2 + .../middleware/DomComponentsMiddleware.ts | 14 --- .../__tests__/DomComponentsMiddleware-test.ts | 85 ------------------- 3 files changed, 2 insertions(+), 99 deletions(-) delete mode 100644 packages/@expo/cli/src/start/server/middleware/__tests__/DomComponentsMiddleware-test.ts diff --git a/packages/@expo/cli/CHANGELOG.md b/packages/@expo/cli/CHANGELOG.md index 83e5a0f7c14a6c..1acf2a502d6780 100644 --- a/packages/@expo/cli/CHANGELOG.md +++ b/packages/@expo/cli/CHANGELOG.md @@ -8,6 +8,8 @@ ### ๐Ÿ› Bug fixes +- Drop obsolete webview installation check ([#45489](https://github.com/expo/expo/pull/45489) by [@kitten](https://github.com/kitten)) + ### ๐Ÿ’ก Others - Bump to `@expo/xcpretty@^4.4.4` ([#45473](https://github.com/expo/expo/pull/45473) by [@EvanBacon](https://github.com/EvanBacon)) diff --git a/packages/@expo/cli/src/start/server/middleware/DomComponentsMiddleware.ts b/packages/@expo/cli/src/start/server/middleware/DomComponentsMiddleware.ts index f162bcc2e99682..32f4d4bb74c7f6 100644 --- a/packages/@expo/cli/src/start/server/middleware/DomComponentsMiddleware.ts +++ b/packages/@expo/cli/src/start/server/middleware/DomComponentsMiddleware.ts @@ -7,24 +7,12 @@ import type { ExpoMetroOptions } from './metroOptions'; import { createBundleUrlPath } from './metroOptions'; import type { ServerRequest, ServerResponse } from './server.types'; import { toPosixPath } from '../../../utils/filePath'; -import { memoize } from '../../../utils/fn'; import { fileURLToFilePath } from '../metro/createServerComponentsMiddleware'; export type PickPartial = Omit & Partial>; export const DOM_COMPONENTS_BUNDLE_DIR = 'www.bundle'; -const checkWebViewInstalled = memoize((projectRoot: string) => { - const webViewInstalled = - resolveFrom.silent(projectRoot, 'react-native-webview') || - resolveFrom.silent(projectRoot, '@expo/dom-webview'); - if (!webViewInstalled) { - throw new Error( - `To use DOM Components, you must install the 'react-native-webview' package. Run 'npx expo install react-native-webview' to install it.` - ); - } -}); - type CreateDomComponentsMiddlewareOptions = { /** The absolute project root, used to resolve the `expo/dom/entry.js` path */ projectRoot: string; @@ -53,8 +41,6 @@ export function createDomComponentsMiddleware( return res.end(); } - checkWebViewInstalled(projectRoot); - // NOTE(@kitten): Keep in sync with `src/export/exportDomComponents.ts` // Generate a unique entry file for the webview. const virtualEntry = resolveFrom(projectRoot, 'expo/dom/entry.js'); diff --git a/packages/@expo/cli/src/start/server/middleware/__tests__/DomComponentsMiddleware-test.ts b/packages/@expo/cli/src/start/server/middleware/__tests__/DomComponentsMiddleware-test.ts deleted file mode 100644 index 31c0d56cf41243..00000000000000 --- a/packages/@expo/cli/src/start/server/middleware/__tests__/DomComponentsMiddleware-test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { getMetroServerRoot } from '@expo/config/paths'; -import path from 'path'; -import resolveFrom from 'resolve-from'; - -import { createDomComponentsMiddleware } from '../DomComponentsMiddleware'; -import type { ServerNext, ServerRequest, ServerResponse } from '../server.types'; - -jest.mock('resolve-from'); - -const asRequest = (req: Partial) => req as ServerRequest; - -function createMockResponse() { - return { - getHeader: jest.fn(), - setHeader: jest.fn(), - on: jest.fn(), - once: jest.fn(), - emit: jest.fn(), - end: jest.fn(), - statusCode: 200, - } as unknown as ServerResponse; -} - -describe('Check webview package installation', () => { - const projectRoot = '/project'; - const metroRoot = getMetroServerRoot(projectRoot); - - const mockResolveFrom = resolveFrom as jest.MockedFunction; - mockResolveFrom.mockImplementation((fromDirectory: string, moduleId: string) => { - if (moduleId === 'expo/dom/entry.js') { - return path.join(fromDirectory, 'node_modules/expo/dom/entry.js'); - } - throw new Error(`Cannot resolve module '${moduleId}' from '${fromDirectory}'`); - }); - const mockResolveSilent = resolveFrom.silent as jest.MockedFunction; - const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); - - let req: ServerRequest; - let res: ServerResponse; - let next: jest.Mock; - let middleware: ReturnType; - - beforeEach(() => { - req = asRequest({ - url: 'http://localhost:8081/_expo/@dom/hello.tsx?file=file:///path/to/file.js', - headers: { host: 'localhost:8081' }, - }); - res = createMockResponse(); - next = jest.fn(); - middleware = createDomComponentsMiddleware( - { metroRoot, projectRoot }, - { - mode: 'development', - routerRoot: projectRoot, - reactCompiler: false, - isExporting: false, - } - ); - }); - - afterEach(() => { - mockResolveSilent.mockReset(); - }); - - afterAll(() => { - consoleWarnSpy.mockRestore(); - }); - - it('should show an error message if the webview package is not installed', () => { - mockResolveSilent.mockReturnValue(undefined); - expect(() => middleware(req, res, next)).toThrow( - /To use DOM Components, you must install the 'react-native-webview' package. Run 'npx expo install react-native-webview' to install it./ - ); - }); - - it('should not show error messages if react-native-webview is installed', () => { - mockResolveSilent.mockReturnValue('/project/node_modules/react-native-webview'); - expect(() => middleware(req, res, next)).not.toThrow(); - }); - - it('should not show error messages if @expo/dom-webview is installed', () => { - mockResolveSilent.mockReturnValue('/project/node_modules/@expo/dom-webview'); - expect(() => middleware(req, res, next)).not.toThrow(); - }); -}); From 055ec749912fa688806fb22766d127bd8f0843c9 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Thu, 7 May 2026 12:45:12 +0100 Subject: [PATCH 10/24] test(cli/e2e): Update icons fixtures for SDK 56 (#45496) # Why # How # Test Plan # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/@expo/cli/e2e/__tests__/export-embed-test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/@expo/cli/e2e/__tests__/export-embed-test.ts b/packages/@expo/cli/e2e/__tests__/export-embed-test.ts index 26378cc4b79663..42e6014c36bab1 100644 --- a/packages/@expo/cli/e2e/__tests__/export-embed-test.ts +++ b/packages/@expo/cli/e2e/__tests__/export-embed-test.ts @@ -389,13 +389,7 @@ it('runs `npx expo export:embed --platform android` with source maps', async () 'output.js.map', 'raw/__e2e___staticrendering_sweet.ttf', - expect.stringMatching(/raw\/.*_100thin\.ttf$/), - expect.stringMatching(/raw\/.*_200extralight\.ttf$/), - expect.stringMatching(/raw\/.*_300light\.ttf$/), expect.stringMatching(/raw\/.*_400regular\.ttf$/), - expect.stringMatching(/raw\/.*_500medium\.ttf$/), - expect.stringMatching(/raw\/.*_600semibold\.ttf$/), - expect.stringMatching(/raw\/.*_700bold\.ttf$/), expect.stringMatching(/raw\/.*_evilicons\.ttf$/), 'raw/keep.xml', From a32243d6d98f6a21aaaed527642cf1454157546a Mon Sep 17 00:00:00 2001 From: Wiktor Smaga Date: Thu, 7 May 2026 14:00:43 +0200 Subject: [PATCH 11/24] [iOS][calendar][next] Add support for writeOnly permissions (#44967) # Why Adds an option to request only write permissions, requested in this issue: #33002 # How - Added a `writeOnlyAccess` prop to the config plugin - Added `CalendarWriteOnlyNextPermissionsRequester` and added other, corresponding requesters to the next package - Renamed: ExpoCalendarPermissions to `CalendarAccessGuard`, and added `CalendarPermissionRequester` to move the responsibilities from the module class. - Removed throwing RCTFatal in the legacy module when FullAccess permissions are not defined in the infoPlist # Test Plan - Added a new test screen to NCL - Tested the config plugin using: `et gba --useLocalTemplate -n test-app expo-calendar` with this screen: [calendar.tsx](https://github.com/user-attachments/files/27474671/calendar.tsx) Results: `writeOnlyAccess`: `true`: https://github.com/user-attachments/assets/09c6bada-fa29-4fd9-a600-93f4b76ffcc2 `writeOnlyAccess`: `false`: https://github.com/user-attachments/assets/aaff4658-6c3b-4aa8-a6b1-2525046fb5a9 --- .../src/navigation/ExpoApisStackNavigator.tsx | 6 +- .../Calendar@Next/CalendarNextScreens.tsx | 49 +++++ .../CalendarsNextScreen.tsx | 30 +-- .../{ => Calendar@Next}/EventsNextScreen.tsx | 8 +- .../RemindersNextScreen.tsx | 0 .../WriteOnlyPermissionsScreen.tsx | 195 ++++++++++++++++++ packages/expo-calendar/CHANGELOG.md | 1 + .../calendar/next/CalendarNextModule.kt | 11 +- .../expo-calendar/build/next/Calendar.d.ts | 22 +- .../build/next/Calendar.d.ts.map | 2 +- packages/expo-calendar/build/next/Calendar.js | 10 +- .../expo-calendar/build/next/Calendar.js.map | 2 +- .../build/next/ExpoCalendar.d.ts | 4 +- .../build/next/ExpoCalendar.d.ts.map | 2 +- .../build/next/ExpoCalendar.js.map | 2 +- .../build/next/ExpoGoCalendarNextStub.d.ts | 4 +- .../next/ExpoGoCalendarNextStub.d.ts.map | 2 +- .../build/next/ExpoGoCalendarNextStub.js | 4 +- .../build/next/ExpoGoCalendarNextStub.js.map | 2 +- ...ssions.swift => CalendarAccessGuard.swift} | 46 +++-- .../ios/Next/CalendarNextModule.swift | 83 ++++---- .../Next/CalendarPermissionRequester.swift | 47 +++++ .../ios/Next/CalendarPlistKeys.swift | 21 ++ .../CalendarNextPermissionsRequester.swift | 72 +++++++ ...darWriteOnlyNextPermissionsRequester.swift | 69 +++++++ .../RemindersNextPermissionsRequester.swift | 58 ++++++ .../CalendarPermissionsRequester.swift | 1 - .../RemindersPermissionsRequester.swift | 1 - .../plugin/build/withCalendar.d.ts | 15 ++ .../plugin/build/withCalendar.js | 23 ++- .../expo-calendar/plugin/src/withCalendar.ts | 39 +++- packages/expo-calendar/src/next/Calendar.ts | 17 +- .../expo-calendar/src/next/ExpoCalendar.ts | 4 +- .../src/next/ExpoGoCalendarNextStub.ts | 4 +- 34 files changed, 721 insertions(+), 135 deletions(-) create mode 100644 apps/native-component-list/src/screens/Calendar@Next/CalendarNextScreens.tsx rename apps/native-component-list/src/screens/{ => Calendar@Next}/CalendarsNextScreen.tsx (89%) rename apps/native-component-list/src/screens/{ => Calendar@Next}/EventsNextScreen.tsx (97%) rename apps/native-component-list/src/screens/{ => Calendar@Next}/RemindersNextScreen.tsx (100%) create mode 100644 apps/native-component-list/src/screens/Calendar@Next/WriteOnlyPermissionsScreen.tsx rename packages/expo-calendar/ios/Next/{ExpoCalendarPermissions.swift => CalendarAccessGuard.swift} (60%) create mode 100644 packages/expo-calendar/ios/Next/CalendarPermissionRequester.swift create mode 100644 packages/expo-calendar/ios/Next/CalendarPlistKeys.swift create mode 100644 packages/expo-calendar/ios/Next/Requesters/CalendarNextPermissionsRequester.swift create mode 100644 packages/expo-calendar/ios/Next/Requesters/CalendarWriteOnlyNextPermissionsRequester.swift create mode 100644 packages/expo-calendar/ios/Next/Requesters/RemindersNextPermissionsRequester.swift diff --git a/apps/native-component-list/src/navigation/ExpoApisStackNavigator.tsx b/apps/native-component-list/src/navigation/ExpoApisStackNavigator.tsx index 6003d6f18054e9..a0d733e1b69fab 100644 --- a/apps/native-component-list/src/navigation/ExpoApisStackNavigator.tsx +++ b/apps/native-component-list/src/navigation/ExpoApisStackNavigator.tsx @@ -9,7 +9,7 @@ import TabIcon from '../components/TabIcon'; import getStackNavWithConfig from '../navigation/StackConfig'; import { AudioScreens } from '../screens/Audio/AudioScreen'; import { BlobScreens } from '../screens/Blob/BlobScreen'; -import { CalendarsNextScreens } from '../screens/CalendarsNextScreen'; +import { CalendarNextScreens } from '../screens/Calendar@Next/CalendarNextScreens'; import { CalendarsScreens } from '../screens/CalendarsScreen'; import { apiScreensToListElements } from '../screens/ComponentListScreen'; import { ContactsScreens } from '../screens/Contacts/ContactsScreen'; @@ -218,7 +218,7 @@ export const ScreensList: ScreenConfig[] = [ }, { getComponent() { - return optionalRequire(() => require('../screens/CalendarsNextScreen')); + return optionalRequire(() => require('../screens/Calendar@Next/CalendarNextScreens')); }, name: 'Calendars@next', }, @@ -500,7 +500,7 @@ export const Screens: ScreenConfig[] = [ ...ContactsScreens, ...ContactsNextScreens, ...CalendarsScreens, - ...CalendarsNextScreens, + ...CalendarNextScreens, ...CryptoScreens, ...WorkletsScreens, ]; diff --git a/apps/native-component-list/src/screens/Calendar@Next/CalendarNextScreens.tsx b/apps/native-component-list/src/screens/Calendar@Next/CalendarNextScreens.tsx new file mode 100644 index 00000000000000..16960f2b8fa428 --- /dev/null +++ b/apps/native-component-list/src/screens/Calendar@Next/CalendarNextScreens.tsx @@ -0,0 +1,49 @@ +import { Platform } from 'react-native'; + +import { optionalRequire } from '../../navigation/routeBuilder'; +import ComponentListScreen, { apiScreensToListElements } from '../ComponentListScreen'; + +const calendarTopLevelScreens = [ + { + name: 'CalendarsNextList', + route: 'calendar@next/calendars', + getComponent() { + return optionalRequire(() => require('./CalendarsNextScreen')); + }, + }, +]; + +if (Platform.OS === 'ios') { + calendarTopLevelScreens.push({ + name: 'WriteOnly Permissions', + route: 'calendar@next/write-only-permissions', + getComponent() { + return optionalRequire(() => require('./WriteOnlyPermissionsScreen')); + }, + }); +} + +export const CalendarNextScreens = [ + ...calendarTopLevelScreens, + { + name: 'EventsNext', + route: 'events-next', + options: {}, + getComponent() { + return optionalRequire(() => require('./EventsNextScreen')); + }, + }, + { + name: 'RemindersNext', + route: 'reminders-next', + options: {}, + getComponent() { + return optionalRequire(() => require('./RemindersNextScreen')); + }, + }, +]; + +export default function CalendarNextScreen() { + const apis = apiScreensToListElements(calendarTopLevelScreens); + return ; +} diff --git a/apps/native-component-list/src/screens/CalendarsNextScreen.tsx b/apps/native-component-list/src/screens/Calendar@Next/CalendarsNextScreen.tsx similarity index 89% rename from apps/native-component-list/src/screens/CalendarsNextScreen.tsx rename to apps/native-component-list/src/screens/Calendar@Next/CalendarsNextScreen.tsx index 5a7f56e6b34596..d39cd04b35d8a9 100644 --- a/apps/native-component-list/src/screens/CalendarsNextScreen.tsx +++ b/apps/native-component-list/src/screens/Calendar@Next/CalendarsNextScreen.tsx @@ -13,31 +13,11 @@ import { import { useState } from 'react'; import { Alert, Platform, ScrollView, StyleSheet, View } from 'react-native'; -import Button from '../components/Button'; -import HeadingText from '../components/HeadingText'; -import ListButton from '../components/ListButton'; -import MonoText from '../components/MonoText'; -import Colors from '../constants/Colors'; -import { optionalRequire } from '../navigation/routeBuilder'; - -export const CalendarsNextScreens = [ - { - name: 'EventsNext', - route: 'events-next', - options: {}, - getComponent() { - return optionalRequire(() => require('./EventsNextScreen')); - }, - }, - { - name: 'RemindersNext', - route: 'reminders-next', - options: {}, - getComponent() { - return optionalRequire(() => require('./RemindersNextScreen')); - }, - }, -]; +import Button from '../../components/Button'; +import HeadingText from '../../components/HeadingText'; +import ListButton from '../../components/ListButton'; +import MonoText from '../../components/MonoText'; +import Colors from '../../constants/Colors'; type StackNavigation = StackNavigationProp<{ RemindersNext: { calendar: ExpoCalendar }; diff --git a/apps/native-component-list/src/screens/EventsNextScreen.tsx b/apps/native-component-list/src/screens/Calendar@Next/EventsNextScreen.tsx similarity index 97% rename from apps/native-component-list/src/screens/EventsNextScreen.tsx rename to apps/native-component-list/src/screens/Calendar@Next/EventsNextScreen.tsx index a84d539b4ba583..dea8955d16323f 100644 --- a/apps/native-component-list/src/screens/EventsNextScreen.tsx +++ b/apps/native-component-list/src/screens/Calendar@Next/EventsNextScreen.tsx @@ -4,10 +4,10 @@ import { AddEventWithFormOptions, ExpoCalendar, ExpoCalendarEvent } from 'expo-c import React, { useState, useEffect } from 'react'; import { Alert, Platform, ScrollView, StyleSheet, Text, View } from 'react-native'; -import Button from '../components/Button'; -import HeadingText from '../components/HeadingText'; -import ListButton from '../components/ListButton'; -import MonoText from '../components/MonoText'; +import Button from '../../components/Button'; +import HeadingText from '../../components/HeadingText'; +import ListButton from '../../components/ListButton'; +import MonoText from '../../components/MonoText'; type EventRowProps = { event: ExpoCalendarEvent; diff --git a/apps/native-component-list/src/screens/RemindersNextScreen.tsx b/apps/native-component-list/src/screens/Calendar@Next/RemindersNextScreen.tsx similarity index 100% rename from apps/native-component-list/src/screens/RemindersNextScreen.tsx rename to apps/native-component-list/src/screens/Calendar@Next/RemindersNextScreen.tsx diff --git a/apps/native-component-list/src/screens/Calendar@Next/WriteOnlyPermissionsScreen.tsx b/apps/native-component-list/src/screens/Calendar@Next/WriteOnlyPermissionsScreen.tsx new file mode 100644 index 00000000000000..d42ccfdf28903f --- /dev/null +++ b/apps/native-component-list/src/screens/Calendar@Next/WriteOnlyPermissionsScreen.tsx @@ -0,0 +1,195 @@ +import { + type ExpoCalendar, + getCalendarPermissions, + presentPicker, + requestCalendarPermissions, + useCalendarPermissions, +} from 'expo-calendar/next'; +import { type ReactNode, useState } from 'react'; +import { Alert, ScrollView, StyleSheet, Text, View } from 'react-native'; + +import Button from '../../components/Button'; +import HeadingText from '../../components/HeadingText'; + +type StepProps = { + number: number; + title: string; + description: string; + children?: ReactNode; +}; + +function Step({ number, title, description, children }: StepProps) { + return ( + + {`${number}. ${title}`} + {description} + {children} + + ); +} + +export default function WriteOnlyPermissionsScreen() { + const [calendar, setCalendar] = useState(null); + const [, requestWriteOnlyPermission] = useCalendarPermissions({ + writeOnly: true, + }); + + const pickWriteOnlyCalendar = async () => { + try { + const calendar = await presentPicker(); + if (calendar) { + setCalendar(calendar); + Alert.alert('Calendar picked', `Title: ${calendar.title}\nID: ${calendar.id}`); + } else { + Alert.alert('Calendar picker cancelled'); + } + } catch (e: any) { + Alert.alert('Failed to pick calendar', e.message); + } + }; + + const requestWriteOnlyAccess = async () => { + try { + const output = await requestWriteOnlyPermission(); + Alert.alert('Write-Only Permission Status', JSON.stringify(output, null, 2)); + } catch (e: any) { + Alert.alert('Failed to request write-only permission', e.message); + } + }; + + const checkWriteOnlyPermission = async () => { + try { + const output = await getCalendarPermissions(true); + Alert.alert('Write-Only Permission Status', JSON.stringify(output, null, 2)); + } catch (e: any) { + Alert.alert('Failed to check write-only permission', e.message); + } + }; + + const checkFullAccessPermission = async () => { + try { + const output = await getCalendarPermissions(); + Alert.alert('Full Access Permission Status', JSON.stringify(output, null, 2)); + } catch (e: any) { + Alert.alert('Failed to check full access permission', e.message); + } + }; + + const requestFullAccessPermission = async () => { + try { + const output = await requestCalendarPermissions(); + Alert.alert('Full Access Permission Status', JSON.stringify(output, null, 2)); + } catch (e: any) { + Alert.alert('Failed to request full access', e.message); + } + }; + + const addEventToCalendar = async () => { + if (!calendar) { + Alert.alert('No calendar', 'Pick a calendar first.'); + return; + } + try { + const timeInOneHour = new Date(); + timeInOneHour.setHours(timeInOneHour.getHours() + 1); + const event = await calendar.createEvent({ + title: 'Write-Only Test Event', + startDate: new Date(), + endDate: timeInOneHour, + timeZone: 'America/Los_Angeles', + }); + Alert.alert('Event created', `ID: ${event.id}`); + } catch (e: any) { + Alert.alert('Failed to create event', e.message); + } + }; + + return ( + + Write-Only Permissions + + Follow these steps on iOS 17+ to verify that write-only calendar permissions can create + events without first granting full calendar access. + + + +