| author | |
| committer | |
| log | bdf5aed665a91f2e1755e6a8ef72cd29627fa937 |
| tree | 98b44932511d2192b2e75fd429a811dff9d2921b |
| parent | 0b62e2eb116abaaaafcb839c24accbbf5aba6a3c |
| signature | Signed by PGP key B5690EEEBB952194 |
4 files changed, 29 insertions(+), 12 deletions(-)
apps/mobile/app.config.ts+6-3| ... | @@ -159,6 +159,12 @@ const config: ExpoConfig = { | ... | @@ -159,6 +159,12 @@ const config: ExpoConfig = { |
| 159 | }, | 159 | }, |
| 160 | ], | 160 | ], |
| 161 | "./plugins/withIosCocoaPodsUuidCache.cjs", | 161 | "./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", | ||
| 162 | [ | 168 | [ |
| 163 | "expo-widgets", | 169 | "expo-widgets", |
| 164 | { | 170 | { |
| ... | @@ -178,9 +184,6 @@ const config: ExpoConfig = { | ... | @@ -178,9 +184,6 @@ const config: ExpoConfig = { |
| 178 | ], | 184 | ], |
| 179 | }, | 185 | }, |
| 180 | ], | 186 | ], |
| 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", | ||
| 184 | "./plugins/withIosSceneLifecycle.cjs", | 187 | "./plugins/withIosSceneLifecycle.cjs", |
| 185 | "./plugins/withAndroidCleartextTraffic.cjs", | 188 | "./plugins/withAndroidCleartextTraffic.cjs", |
| 186 | ], | 189 | ], |
apps/mobile/plugins/lib/addWidgetAssetCatalog.cjs+13-5| ... | @@ -9,8 +9,10 @@ | ... | @@ -9,8 +9,10 @@ |
| 9 | // drops the compiled Assets.car into the extension bundle. Marked | 9 | // drops the compiled Assets.car into the extension bundle. Marked |
| 10 | // alwaysOutOfDate so the build system always runs it. | 10 | // alwaysOutOfDate so the build system always runs it. |
| 11 | // | 11 | // |
| 12 | // Idempotent across re-runs. Returns true when it added the phase, false when it | 12 | // Idempotent across re-runs. Returns true when it added the phase, false when |
| 13 | // was already present or the target was not found. | 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. | ||
| 14 | 16 | ||
| 15 | const PHASE_NAME = "Compile Widget Assets"; | 17 | const PHASE_NAME = "Compile Widget Assets"; |
| 16 | 18 | ||
| ... | @@ -20,8 +22,8 @@ const ACTOOL_SCRIPT = [ | ... | @@ -20,8 +22,8 @@ const ACTOOL_SCRIPT = [ |
| 20 | "set -e", | 22 | "set -e", |
| 21 | 'CATALOG="${SRCROOT}/ExpoWidgetsTarget/Assets.xcassets"', | 23 | 'CATALOG="${SRCROOT}/ExpoWidgetsTarget/Assets.xcassets"', |
| 22 | 'if [ ! -d "$CATALOG" ]; then', | 24 | 'if [ ! -d "$CATALOG" ]; then', |
| 23 | ' echo "warning: widget asset catalog not found at $CATALOG"', | 25 | ' echo "error: widget asset catalog not found at $CATALOG (expo-widgets wiped it? check plugin ordering in app.config.ts)"', |
| 24 | " exit 0", | 26 | " exit 1", |
| 25 | "fi", | 27 | "fi", |
| 26 | 'DEST="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"', | 28 | 'DEST="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"', |
| 27 | 'mkdir -p "$DEST"', | 29 | 'mkdir -p "$DEST"', |
| ... | @@ -51,7 +53,13 @@ function findByName(map, name) { | ... | @@ -51,7 +53,13 @@ function findByName(map, name) { |
| 51 | function addWidgetAssetCatalog(proj, opts) { | 53 | function addWidgetAssetCatalog(proj, opts) { |
| 52 | const objects = proj.hash.project.objects; | 54 | const objects = proj.hash.project.objects; |
| 53 | const target = findByName(objects.PBXNativeTarget, opts.targetName); | 55 | 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 | } | ||
| 55 | 63 | ||
| 56 | const phases = target.value.buildPhases || []; | 64 | const phases = target.value.buildPhases || []; |
| 57 | const existing = objects.PBXShellScriptBuildPhase || {}; | 65 | const existing = objects.PBXShellScriptBuildPhase || {}; |
apps/mobile/plugins/withWidgetLogoAsset.cjs+9-3| ... | @@ -5,9 +5,15 @@ | ... | @@ -5,9 +5,15 @@ |
| 5 | // expo-widgets generates ExpoWidgetsTarget without a Resources build phase and | 5 | // expo-widgets generates ExpoWidgetsTarget without a Resources build phase and |
| 6 | // has no asset support, so this plugin (a) writes an SVG template image set into | 6 | // has no asset support, so this plugin (a) writes an SVG template image set into |
| 7 | // the generated widget asset catalog and (b) wires that catalog into the widget | 7 | // 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 | 8 | // target with an actool build phase. |
| 9 | // survive `expo prebuild --clean`. Must be listed AFTER "expo-widgets" in the | 9 | // |
| 10 | // plugins array so the widget target exists when this runs. | 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. | ||
| 11 | 17 | ||
| 12 | const path = require("path"); | 18 | const path = require("path"); |
| 13 | const fs = require("fs"); | 19 | const fs = require("fs"); |
apps/mobile/scripts/wire-widget-asset-catalog.cjs+1-1| ... | @@ -26,5 +26,5 @@ if (added) { | ... | @@ -26,5 +26,5 @@ if (added) { |
| 26 | fs.writeFileSync(pbxprojPath, proj.writeSync()); | 26 | fs.writeFileSync(pbxprojPath, proj.writeSync()); |
| 27 | console.log("Added widget asset-compile phase to ExpoWidgetsTarget."); | 27 | console.log("Added widget asset-compile phase to ExpoWidgetsTarget."); |
| 28 | } else { | 28 | } else { |
| 29 | console.log("No change: phase already present or target not found."); | 29 | console.log("No change: phase already present."); |
| 30 | } | 30 | } |