authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-07 02:00:10-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-07 02:00:10-07:00
logbdf5aed665a91f2e1755e6a8ef72cd29627fa937
tree98b44932511d2192b2e75fd429a811dff9d2921b
parent0b62e2eb116abaaaafcb839c24accbbf5aba6a3c
signature Signed by PGP key B5690EEEBB952194

Fix Expo widget asset wiring order (#3763)


4 files changed, 29 insertions(+), 12 deletions(-)

apps/mobile/app.config.ts+6-3
......@@ -159,6 +159,12 @@ const config: ExpoConfig = {
159159 },
160160 ],
161161 "./plugins/withIosCocoaPodsUuidCache.cjs",
162 // Must be listed BEFORE expo-widgets: same-type mods run last-registered-
163 // first, so registering earlier makes this plugin's mods run AFTER
164 // expo-widgets' — its dangerous mod wipes ios/ExpoWidgetsTarget/ (which
165 // would delete the asset catalog) and its xcodeproj mod creates the widget
166 // target (which must exist before the compile phase can be attached).
167 "./plugins/withWidgetLogoAsset.cjs",
162168 [
163169 "expo-widgets",
164170 {
......@@ -178,9 +184,6 @@ const config: ExpoConfig = {
178184 ],
179185 },
180186 ],
181 // Must run after expo-widgets so the widget target exists when it wires in
182 // the branded logo asset catalog.
183 "./plugins/withWidgetLogoAsset.cjs",
184187 "./plugins/withIosSceneLifecycle.cjs",
185188 "./plugins/withAndroidCleartextTraffic.cjs",
186189 ],
apps/mobile/plugins/lib/addWidgetAssetCatalog.cjs+13-5
......@@ -9,8 +9,10 @@
99// drops the compiled Assets.car into the extension bundle. Marked
1010// alwaysOutOfDate so the build system always runs it.
1111//
12// Idempotent across re-runs. Returns true when it added the phase, false when it
13// was already present or the target was not found.
12// Idempotent across re-runs. Returns true when it added the phase, false when
13// it was already present. Throws when the target does not exist — that means
14// this ran before expo-widgets created the target (plugin ordering bug) and
15// silently skipping would ship a widget without its assets.
1416
1517const PHASE_NAME = "Compile Widget Assets";
1618
......@@ -20,8 +22,8 @@ const ACTOOL_SCRIPT = [
2022 "set -e",
2123 'CATALOG="${SRCROOT}/ExpoWidgetsTarget/Assets.xcassets"',
2224 'if [ ! -d "$CATALOG" ]; then',
23 ' echo "warning: widget asset catalog not found at $CATALOG"',
24 " exit 0",
25 ' echo "error: widget asset catalog not found at $CATALOG (expo-widgets wiped it? check plugin ordering in app.config.ts)"',
26 " exit 1",
2527 "fi",
2628 'DEST="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"',
2729 'mkdir -p "$DEST"',
......@@ -51,7 +53,13 @@ function findByName(map, name) {
5153function addWidgetAssetCatalog(proj, opts) {
5254 const objects = proj.hash.project.objects;
5355 const target = findByName(objects.PBXNativeTarget, opts.targetName);
54 if (!target) return false;
56 if (!target) {
57 throw new Error(
58 `addWidgetAssetCatalog: target "${opts.targetName}" not found — ` +
59 "withWidgetLogoAsset must be registered before expo-widgets so its " +
60 "xcodeproj mod runs after the widget target is created.",
61 );
62 }
5563
5664 const phases = target.value.buildPhases || [];
5765 const existing = objects.PBXShellScriptBuildPhase || {};
apps/mobile/plugins/withWidgetLogoAsset.cjs+9-3
......@@ -5,9 +5,15 @@
55// expo-widgets generates ExpoWidgetsTarget without a Resources build phase and
66// has no asset support, so this plugin (a) writes an SVG template image set into
77// the generated widget asset catalog and (b) wires that catalog into the widget
8// target with a dedicated Resources build phase. Both steps are idempotent and
9// survive `expo prebuild --clean`. Must be listed AFTER "expo-widgets" in the
10// plugins array so the widget target exists when this runs.
8// target with an actool build phase.
9//
10// ORDERING: must be listed BEFORE "expo-widgets" in the plugins array. Expo
11// chains same-type mods so the last-registered runs FIRST; registering this
12// plugin earlier makes its mods run AFTER expo-widgets' mods. That matters
13// twice: expo-widgets' dangerous mod rmSync's ios/ExpoWidgetsTarget/ (deleting
14// any catalog written before it), and its xcodeproj mod is what creates the
15// widget target. Listed after expo-widgets, both steps silently no-op on a
16// fresh prebuild — which is how prod build 8 shipped without the logo.
1117
1218const path = require("path");
1319const fs = require("fs");
apps/mobile/scripts/wire-widget-asset-catalog.cjs+1-1
......@@ -26,5 +26,5 @@ if (added) {
2626 fs.writeFileSync(pbxprojPath, proj.writeSync());
2727 console.log("Added widget asset-compile phase to ExpoWidgetsTarget.");
2828} else {
29 console.log("No change: phase already present or target not found.");
29 console.log("No change: phase already present.");
3030}