Expo Prebuild — Phase 1, PR 1: scaffold app.config.ts + config plugins#7544
Draft
hawkrives wants to merge 8 commits into
Draft
Expo Prebuild — Phase 1, PR 1: scaffold app.config.ts + config plugins#7544hawkrives wants to merge 8 commits into
hawkrives wants to merge 8 commits into
Conversation
…dencies Preparing for Expo Prebuild migration Phase 1. These packages are consumed by app.config.ts and the local config plugins authored in subsequent commits. No runtime behavior change. Versions pinned to match what Expo SDK 53 bundles internally: - @expo/config-plugins@~10.1.2 (Expo 53 declares ~10.1.2 as a dep) - expo-build-properties@~0.14.6 (SDK 53-compatible series) The default `npm install --save-dev <pkg>` resolved to 55.x for both, which is the SDK 55 line and would conflict with expo prebuild's nested copy of config-plugins@10.1.x. Pinning ensures our plugin imports resolve to the same version expo prebuild loads. Pre-flight findings (Task 0 of the PR 1 scaffolding plan): - react-native-change-icon plugin status: Finding C — installed v5.0.0 ships no plugin; npm latest is also 5.0.0. Task 5 proceeds as written (author with-alternate-icons.ts locally). - use_native_modules! without community CLI: Deferred — verification requires `pod install` (macOS-only). Will let CI on the implementation branch exercise it; revisit before PR 2 finalizes removal of @react-native-community/cli devDeps. - @react-native-vector-icons plugin shape: each font package (@react-native-vector-icons/{entypo,ionicons,material-design-icons}) ships its own app.plugin.js that registers its single font via withInfoPlist. Plan A's assumption of a centralized '@react-native-vector-icons/common/plugin' was incorrect. Task 3 will register each font package individually as a string-form plugin. See docs/superpowers/plans/2026-04-16-expo-prebuild-pr1-scaffolding.md
Declares the iOS fields from the design spec (Section A of the Config Plugin Inventory) that previously lived in Info.plist, pbxproj, or as hand-managed constants. Plugins array is empty and wired up in subsequent commits. A Jest test at __tests__/app-config.test.ts asserts on the exported shape so this config can't silently drift. No behavior change: expo prebuild is not yet invoked, so app.config.ts has no effect on the build yet. See docs/superpowers/specs/2026-04-16-expo-prebuild-migration-design.md
Adds library-provided config plugins to app.config.ts:
- expo-build-properties: sets iOS deployment target to 14.0 and pins
newArchEnabled to false. Replaces the hand-edited Podfile that
currently sets `ENV['RCT_NEW_ARCH_ENABLED'] = '0'` at the top-level
(removed when ios/ is regenerated in PR 2).
- @react-native-vector-icons/{entypo,ionicons,material-design-icons}:
each font package ships its own app.plugin.js that registers its
single .ttf via withInfoPlist on UIAppFonts. Registering all three
individually replaces the hand-maintained Copy Files build phase
references to ${PODS_ROOT}/../../node_modules/...
(The plan originally assumed a centralized
'@react-native-vector-icons/common/plugin' with iconFontNames
options; Task 0.3 found the real shape is one plugin per font
package, hardcoding its font name.)
Still no behavior change: expo prebuild is not yet invoked.
See docs/superpowers/specs/2026-04-16-expo-prebuild-migration-design.md
Preserves three AppDelegate.swift customizations for the forthcoming
Expo prebuild cutover:
1. --reset-state launch argument handler: scrubs Library/Application
Support/{bundleId}, RCTAsyncLocalStorage_V1, and UserDefaults for
the bundle ID. Used by XCUITests to start from a clean slate.
2. URLCache sizing: 4 MiB memory, 20 MiB disk. Tunes the shared URL
cache for the app's network traffic profile.
3. AVAudioSession.playback category: ensures streaming audio plays
while the device's mute switch is engaged.
Insertion is idempotent (bracketed by marker comments) and anchors on
`self.moduleName = "..."` which Expo's template writes from app.config
`expo.name`. Fails loudly if the anchor is missing rather than producing
a silently-wrong AppDelegate.
The transformation is expressed as pure helpers (`patchAppDelegate`,
`addImportIfMissing`) wrapped by a thin withAppDelegate adapter; 8 unit
tests cover baseline insertion, idempotency, `import AVFoundation`
handling, and the missing-anchor error case.
Plugin is registered with app.config.ts in Task 7. No behavior change
yet.
See docs/superpowers/specs/2026-04-16-expo-prebuild-migration-design.md
Reproduces the existing `icon_type_windmill` alternate-icon wiring as a config plugin with two pure helpers: 1. addAlternateIcons(plist): ensures CFBundleAlternateIcons on both CFBundleIcons and CFBundleIcons~ipad contains the windmill entry (CFBundleIconFiles: ["windmill"], UIPrerenderedIcon: true). 2. addWindmillResources(project): registers the four windmill*.png files (@2x, @3x, @2x~iPad, @3x~iPad) under ios/../images/icons/ as bundle resources on the AllAboutOlaf target. Both transformations are idempotent. 7 unit tests cover baseline insertion, duplicate-avoidance, preservation of unrelated entries, and the missing-target error case. The pbxproj tests load the real ios/AllAboutOlaf.xcodeproj/project.pbxproj as an in-memory fixture (never modified on disk). Also adds @types/xcode@3.0.0 as a devDependency so the xcode npm package (transitive dep of @expo/config-plugins) is properly typed. Plugin is registered with app.config.ts in Task 7. No behavior change yet. See docs/superpowers/specs/2026-04-16-expo-prebuild-migration-design.md
Patches the Podfile to disable Expo autolinking inside the AllAboutOlafUITests target and ensures the UITests PBXNativeTarget exists in the Xcode project, recreating it idempotently when missing.
Wires with-app-delegate-customizations, with-alternate-icons, and with-xcuitest-target into the Expo plugins array so prebuild applies the AAO-specific iOS modifications.
Removes __tests__/app-config.test.ts — its assertions just mirrored the literal values in app.config.ts and caught no real failure mode. Replaces the per-fragment toContain assertions in the three plugin tests with snapshot tests for the happy-path output. Behavioral assertions (idempotency, missing-anchor errors, addImportIfMissing edge cases) stay explicit since snapshots can't express them meaningfully. Also drops plugins/types.ts — a 3-line ConfigPlugin re-export shim — and imports the type directly from @expo/config-plugins in each plugin.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First of three PRs for Phase 1 of the Expo Prebuild migration (design spec in #7542). Scaffolding only — no behavior change. The existing
ios/directory remains authoritative; nothing in this PR runsexpo prebuildor alters the build. The cutover happens in PR 2.This PR introduces the declarative config and the local config plugins that Phase 1 cutover will rely on, all under unit-test coverage.
What's added
app.config.ts— 12 declarative iOS fields ported fromInfo.plist/ project settings (bundle ID, build number, ATS exceptions, status bar style, audio background mode, supported orientations, etc.).expo-build-properties(deployment target 14.0,newArchEnabled: false) and the three@react-native-vector-icons/*font packages.plugins/:with-app-delegate-customizations— patchesAppDelegate.swiftwith the--reset-statelaunch arg handler,URLCachesetup,AVAudioSessionplayback category, andimport AVFoundation. Uses begin/end markers for idempotency.with-alternate-icons— adds theicon_type_windmillalternate icon entry toCFBundleIcons+CFBundleIcons~ipad, and registers the fourwindmill@{2,3}x{,~iPad}.pngresources on the main app target.with-xcuitest-target— patches the Podfile to disable Expo autolinking for the UITests target (nestedinherit! :noneblock +Pod::Podfile::TargetDefinition.prependshim) and ensures theAllAboutOlafUITestsPBXNativeTarget exists in the Xcode project.@expo/config-plugins@~10.1.2,expo-build-properties@~0.14.6,@types/xcode@3.0.0(pinned to the Expo SDK 53–compatible series).Test plan
mise run agent:pre-commit— pretty, lint, tsc, jest all green (235 passed across 40 suites locally).patchAppDelegate,patchPodfileForUITests,addAlternateIcons) tested for happy path, idempotency, and missing-anchor errors.addWindmillResources,ensureUITestTarget) tested against the realios/AllAboutOlaf.xcodeproj/project.pbxprojfixture, including post-strip recreation and double-application idempotency.Notes for review
xcodenpm package'saddTargetwritesname/productNamewrapped in quotes butfindTargetKeydoes an unquoted exact match —ensureUITestTargetstrips the quotes post-creation so subsequent lookups work. Worth a glance atplugins/with-xcuitest-target.ts:120-124.expo-build-properties@latestresolves to v55, which conflicts with Expo SDK 53's peer range. Same story for@expo/config-plugins. Both are pinned to the~range that Expo 53 actually accepts.app.plugin.js), not the centralized@react-native-vector-icons/common/pluginthat the spec originally suggested.https://claude.ai/code/session_01TFkK4X2U6kGX137MerKD8k