@expo/cli 56.1.18 → 56.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/bin/cli CHANGED
@@ -139,7 +139,7 @@ const args = (0, _arg().default)({
139
139
  });
140
140
  if (args['--version']) {
141
141
  // Version is added in the build script.
142
- console.log("56.1.18");
142
+ console.log("56.1.20");
143
143
  process.exit(0);
144
144
  }
145
145
  if (args['--non-interactive']) {
@@ -76,7 +76,7 @@ function getInitMetadata() {
76
76
  return {
77
77
  format: 'v0-jsonl',
78
78
  // Version is added in the build script.
79
- version: "56.1.18" ?? 'UNVERSIONED'
79
+ version: "56.1.20" ?? 'UNVERSIONED'
80
80
  };
81
81
  }
82
82
  function getWellKnownTemporaryLogFile(projectRoot, command) {
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+
6
+ //# sourceMappingURL=static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":""}
@@ -29,6 +29,52 @@ function _interop_require_default(obj) {
29
29
  };
30
30
  }
31
31
  const debug = require('debug')('expo:run:android:resolveInstallApkName');
32
+ function resolveApkFromOutputMetadata(apkVariantDirectory, availableCPUs) {
33
+ const metadataPath = _path().default.join(apkVariantDirectory, 'output-metadata.json');
34
+ let metadata;
35
+ try {
36
+ metadata = JSON.parse(_fs().default.readFileSync(metadataPath, 'utf8'));
37
+ } catch (error) {
38
+ debug('Failed to parse output-metadata.json', error);
39
+ return null;
40
+ }
41
+ const { elements } = metadata;
42
+ if (!(elements == null ? void 0 : elements.length)) {
43
+ return null;
44
+ }
45
+ // ABI split: match by device ABI. Exclude DeviceABI.universal — AGP never uses it as an ABI filter.
46
+ const isAbiSplit = elements.some((e)=>{
47
+ var _e_filters;
48
+ return e == null ? void 0 : (_e_filters = e.filters) == null ? void 0 : _e_filters.some((f)=>(f == null ? void 0 : f.filterType) === 'ABI');
49
+ });
50
+ if (isAbiSplit) {
51
+ for (const cpu of availableCPUs){
52
+ if (cpu === _adb.DeviceABI.universal) {
53
+ continue;
54
+ }
55
+ const match = elements.find((e)=>{
56
+ var _e_filters;
57
+ return e == null ? void 0 : (_e_filters = e.filters) == null ? void 0 : _e_filters.some((f)=>f.filterType === 'ABI' && f.value === cpu);
58
+ });
59
+ const outputFile = match == null ? void 0 : match.outputFile;
60
+ if (typeof outputFile === 'string' && _fs().default.existsSync(_path().default.join(apkVariantDirectory, outputFile))) {
61
+ debug('Resolved ABI-split APK from output-metadata.json:', outputFile);
62
+ return outputFile;
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ if (elements.length === 1) {
68
+ var _elements_;
69
+ const outputFile = (_elements_ = elements[0]) == null ? void 0 : _elements_.outputFile;
70
+ if (typeof outputFile === 'string' && _fs().default.existsSync(_path().default.join(apkVariantDirectory, outputFile))) {
71
+ debug('Resolved APK from output-metadata.json:', outputFile);
72
+ return outputFile;
73
+ }
74
+ }
75
+ // Density/language splits produce multiple elements without ABI filters
76
+ return null;
77
+ }
32
78
  async function resolveInstallApkNameAsync(device, { appName, buildType, flavors, apkVariantDirectory }) {
33
79
  const availableCPUs = await (0, _adb.getDeviceABIsAsync)(device);
34
80
  availableCPUs.push(_adb.DeviceABI.universal);
@@ -50,7 +96,8 @@ async function resolveInstallApkNameAsync(device, { appName, buildType, flavors,
50
96
  if (_fs().default.existsSync(apkPath)) {
51
97
  return apkName;
52
98
  }
53
- return null;
99
+ // Last resort: read AGP's output-metadata.json to handle custom outputFileName overrides.
100
+ return resolveApkFromOutputMetadata(apkVariantDirectory, availableCPUs);
54
101
  }
55
102
  function getApkFileName(appName, buildType, flavors, cpuArch) {
56
103
  let apkName = `${appName}-`;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/run/android/resolveInstallApkName.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nimport type { GradleProps } from './resolveGradlePropsAsync';\nimport type { Device } from '../../start/platforms/android/adb';\nimport { DeviceABI, getDeviceABIsAsync } from '../../start/platforms/android/adb';\n\nconst debug = require('debug')('expo:run:android:resolveInstallApkName') as typeof console.log;\n\nexport async function resolveInstallApkNameAsync(\n device: Pick<Device, 'name' | 'pid'>,\n { appName, buildType, flavors, apkVariantDirectory }: GradleProps\n) {\n const availableCPUs = await getDeviceABIsAsync(device);\n availableCPUs.push(DeviceABI.universal);\n\n debug('Supported ABIs: ' + availableCPUs.join(', '));\n debug('Searching for APK: ' + apkVariantDirectory);\n\n // Check for cpu specific builds first\n for (const availableCPU of availableCPUs) {\n const apkName = getApkFileName(appName, buildType, flavors, availableCPU);\n const apkPath = path.join(apkVariantDirectory, apkName);\n debug('Checking for APK at:', apkPath);\n if (fs.existsSync(apkPath)) {\n return apkName;\n }\n }\n\n // Otherwise use the default apk named after the variant: app-debug.apk\n const apkName = getApkFileName(appName, buildType, flavors);\n const apkPath = path.join(apkVariantDirectory, apkName);\n debug('Checking for fallback APK at:', apkPath);\n if (fs.existsSync(apkPath)) {\n return apkName;\n }\n\n return null;\n}\n\nfunction getApkFileName(\n appName: string,\n buildType: string,\n flavors?: string[] | null,\n cpuArch?: string | null\n) {\n let apkName = `${appName}-`;\n if (flavors) {\n apkName += flavors.reduce((rest, flavor) => `${rest}${flavor}-`, '');\n }\n if (cpuArch) {\n apkName += `${cpuArch}-`;\n }\n apkName += `${buildType}.apk`;\n\n return apkName;\n}\n"],"names":["resolveInstallApkNameAsync","debug","require","device","appName","buildType","flavors","apkVariantDirectory","availableCPUs","getDeviceABIsAsync","push","DeviceABI","universal","join","availableCPU","apkName","getApkFileName","apkPath","path","fs","existsSync","cpuArch","reduce","rest","flavor"],"mappings":";;;;+BASsBA;;;eAAAA;;;;gEATP;;;;;;;gEACE;;;;;;qBAI6B;;;;;;AAE9C,MAAMC,QAAQC,QAAQ,SAAS;AAExB,eAAeF,2BACpBG,MAAoC,EACpC,EAAEC,OAAO,EAAEC,SAAS,EAAEC,OAAO,EAAEC,mBAAmB,EAAe;IAEjE,MAAMC,gBAAgB,MAAMC,IAAAA,uBAAkB,EAACN;IAC/CK,cAAcE,IAAI,CAACC,cAAS,CAACC,SAAS;IAEtCX,MAAM,qBAAqBO,cAAcK,IAAI,CAAC;IAC9CZ,MAAM,wBAAwBM;IAE9B,sCAAsC;IACtC,KAAK,MAAMO,gBAAgBN,cAAe;QACxC,MAAMO,UAAUC,eAAeZ,SAASC,WAAWC,SAASQ;QAC5D,MAAMG,UAAUC,eAAI,CAACL,IAAI,CAACN,qBAAqBQ;QAC/Cd,MAAM,wBAAwBgB;QAC9B,IAAIE,aAAE,CAACC,UAAU,CAACH,UAAU;YAC1B,OAAOF;QACT;IACF;IAEA,uEAAuE;IACvE,MAAMA,UAAUC,eAAeZ,SAASC,WAAWC;IACnD,MAAMW,UAAUC,eAAI,CAACL,IAAI,CAACN,qBAAqBQ;IAC/Cd,MAAM,iCAAiCgB;IACvC,IAAIE,aAAE,CAACC,UAAU,CAACH,UAAU;QAC1B,OAAOF;IACT;IAEA,OAAO;AACT;AAEA,SAASC,eACPZ,OAAe,EACfC,SAAiB,EACjBC,OAAyB,EACzBe,OAAuB;IAEvB,IAAIN,UAAU,GAAGX,QAAQ,CAAC,CAAC;IAC3B,IAAIE,SAAS;QACXS,WAAWT,QAAQgB,MAAM,CAAC,CAACC,MAAMC,SAAW,GAAGD,OAAOC,OAAO,CAAC,CAAC,EAAE;IACnE;IACA,IAAIH,SAAS;QACXN,WAAW,GAAGM,QAAQ,CAAC,CAAC;IAC1B;IACAN,WAAW,GAAGV,UAAU,IAAI,CAAC;IAE7B,OAAOU;AACT"}
1
+ {"version":3,"sources":["../../../../src/run/android/resolveInstallApkName.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nimport type { GradleProps } from './resolveGradlePropsAsync';\nimport type { Device } from '../../start/platforms/android/adb';\nimport { DeviceABI, getDeviceABIsAsync } from '../../start/platforms/android/adb';\n\nconst debug = require('debug')('expo:run:android:resolveInstallApkName') as typeof console.log;\n\ntype OutputMetadataElement = {\n filters?: { filterType: string; value: string }[];\n outputFile: string;\n};\n\ntype OutputMetadata = {\n elements: OutputMetadataElement[];\n};\n\nfunction resolveApkFromOutputMetadata(\n apkVariantDirectory: string,\n availableCPUs: DeviceABI[]\n): string | null {\n const metadataPath = path.join(apkVariantDirectory, 'output-metadata.json');\n let metadata: OutputMetadata;\n try {\n metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));\n } catch (error) {\n debug('Failed to parse output-metadata.json', error);\n return null;\n }\n\n const { elements } = metadata;\n if (!elements?.length) {\n return null;\n }\n\n // ABI split: match by device ABI. Exclude DeviceABI.universal — AGP never uses it as an ABI filter.\n const isAbiSplit = elements.some((e) => e?.filters?.some((f) => f?.filterType === 'ABI'));\n if (isAbiSplit) {\n for (const cpu of availableCPUs) {\n if (cpu === DeviceABI.universal) {\n continue;\n }\n const match = elements.find((e) =>\n e?.filters?.some((f) => f.filterType === 'ABI' && f.value === cpu)\n );\n const outputFile = match?.outputFile;\n if (\n typeof outputFile === 'string' &&\n fs.existsSync(path.join(apkVariantDirectory, outputFile))\n ) {\n debug('Resolved ABI-split APK from output-metadata.json:', outputFile);\n return outputFile;\n }\n }\n return null;\n }\n\n if (elements.length === 1) {\n const outputFile = elements[0]?.outputFile;\n if (\n typeof outputFile === 'string' &&\n fs.existsSync(path.join(apkVariantDirectory, outputFile))\n ) {\n debug('Resolved APK from output-metadata.json:', outputFile);\n return outputFile;\n }\n }\n\n // Density/language splits produce multiple elements without ABI filters\n return null;\n}\n\nexport async function resolveInstallApkNameAsync(\n device: Pick<Device, 'name' | 'pid'>,\n { appName, buildType, flavors, apkVariantDirectory }: GradleProps\n) {\n const availableCPUs = await getDeviceABIsAsync(device);\n availableCPUs.push(DeviceABI.universal);\n\n debug('Supported ABIs: ' + availableCPUs.join(', '));\n debug('Searching for APK: ' + apkVariantDirectory);\n\n // Check for cpu specific builds first\n for (const availableCPU of availableCPUs) {\n const apkName = getApkFileName(appName, buildType, flavors, availableCPU);\n const apkPath = path.join(apkVariantDirectory, apkName);\n debug('Checking for APK at:', apkPath);\n if (fs.existsSync(apkPath)) {\n return apkName;\n }\n }\n\n // Otherwise use the default apk named after the variant: app-debug.apk\n const apkName = getApkFileName(appName, buildType, flavors);\n const apkPath = path.join(apkVariantDirectory, apkName);\n debug('Checking for fallback APK at:', apkPath);\n if (fs.existsSync(apkPath)) {\n return apkName;\n }\n\n // Last resort: read AGP's output-metadata.json to handle custom outputFileName overrides.\n return resolveApkFromOutputMetadata(apkVariantDirectory, availableCPUs);\n}\n\nfunction getApkFileName(\n appName: string,\n buildType: string,\n flavors?: string[] | null,\n cpuArch?: string | null\n) {\n let apkName = `${appName}-`;\n if (flavors) {\n apkName += flavors.reduce((rest, flavor) => `${rest}${flavor}-`, '');\n }\n if (cpuArch) {\n apkName += `${cpuArch}-`;\n }\n apkName += `${buildType}.apk`;\n\n return apkName;\n}\n"],"names":["resolveInstallApkNameAsync","debug","require","resolveApkFromOutputMetadata","apkVariantDirectory","availableCPUs","metadataPath","path","join","metadata","JSON","parse","fs","readFileSync","error","elements","length","isAbiSplit","some","e","filters","f","filterType","cpu","DeviceABI","universal","match","find","value","outputFile","existsSync","device","appName","buildType","flavors","getDeviceABIsAsync","push","availableCPU","apkName","getApkFileName","apkPath","cpuArch","reduce","rest","flavor"],"mappings":"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"}
@@ -12,6 +12,9 @@ _export(exports, {
12
12
  get _assertXcodeBuildResults () {
13
13
  return _assertXcodeBuildResults;
14
14
  },
15
+ get _extractXcodeBuildErrorLines () {
16
+ return _extractXcodeBuildErrorLines;
17
+ },
15
18
  get buildAsync () {
16
19
  return buildAsync;
17
20
  },
@@ -435,10 +438,30 @@ function _assertXcodeBuildResults(code, results, error, xcodeProject, logFilePat
435
438
  if (localizedError) {
436
439
  throwWithMessage(_chalk().default.bold(localizedError) + '\n\n');
437
440
  }
441
+ // `@expo/xcpretty` can leave a real failure uncounted (its compile-error
442
+ // matching is stateful and anchored, and stderr never passes through it), so
443
+ // the build summary reads "0 error(s)" and in CI the full log below is
444
+ // truncated before the real error line.
445
+ const errorLines = _extractXcodeBuildErrorLines(results + '\n' + error);
446
+ if (errorLines.length) {
447
+ throwWithMessage(_chalk().default.red(errorLines.join('\n')) + '\n\n' + results + '\n\n' + error);
448
+ }
438
449
  // Show all the log info because often times the error is coming from a shell script,
439
450
  // that invoked a node script, that started metro, which threw an error.
440
451
  throwWithMessage(results + '\n\n' + error);
441
452
  }
453
+ function _extractXcodeBuildErrorLines(output) {
454
+ const seen = new Set();
455
+ const errors = [];
456
+ for (const raw of output.split(/\r?\n/)){
457
+ const line = raw.trim();
458
+ if (/(?:^|\s)error:\s/.test(line) && !seen.has(line)) {
459
+ seen.add(line);
460
+ errors.push(line);
461
+ }
462
+ }
463
+ return errors;
464
+ }
442
465
  function writeBuildLogs(projectRoot, buildOutput, errorOutput) {
443
466
  const [logFilePath, errorFilePath] = getErrorLogFilePath(projectRoot);
444
467
  _fs().default.writeFileSync(logFilePath, buildOutput);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/run/ios/XcodeBuild.ts"],"sourcesContent":["import spawnAsync from '@expo/spawn-async';\nimport { ExpoRunFormatter } from '@expo/xcpretty';\nimport chalk from 'chalk';\nimport type { SpawnOptionsWithoutStdio } from 'child_process';\nimport { spawn } from 'child_process';\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\n\nimport type { BuildProps, ProjectInfo } from './XcodeBuild.types';\nimport { ensureDeviceIsCodeSignedForDeploymentAsync } from './codeSigning/configureCodeSigning';\nimport { simulatorBuildRequiresCodeSigning } from './codeSigning/simulatorCodeSigning';\nimport * as Log from '../../log';\nimport type { OSType } from '../../start/platforms/ios/simctl';\nimport { ensureDirectory } from '../../utils/dir';\nimport { env } from '../../utils/env';\nimport { AbortCommandError, CommandError } from '../../utils/errors';\nimport { getUserTerminal } from '../../utils/terminal';\n\n// Error messages that indicate concurrent Xcode build failures.\n// When multiple builds run simultaneously, Xcode's build database can become locked.\nconst CONCURRENT_BUILD_ERROR_MESSAGE_1 = 'database is locked';\nconst CONCURRENT_BUILD_ERROR_MESSAGE_2 = 'there are two concurrent builds running';\n\n/** Get the generic Xcode destination string for a given OS type.\n * Used when building without targeting a specific device (build-only workflow).\n */\nexport function getGenericSimulatorDestination(osType: OSType): string {\n switch (osType) {\n case 'tvOS':\n return 'generic/platform=tvOS Simulator';\n case 'watchOS':\n return 'generic/platform=watchOS Simulator';\n case 'xrOS':\n return 'generic/platform=visionOS Simulator';\n case 'iOS':\n default:\n return 'generic/platform=iOS Simulator';\n }\n}\nexport function logPrettyItem(message: string) {\n Log.log(chalk`{whiteBright \\u203A} ${message}`);\n}\n\nexport function matchEstimatedBinaryPath(buildOutput: string): string | null {\n // Match the full path that contains `/(.*)/Developer/Xcode/DerivedData/(.*)/Build/Products/(.*)/(.*).app`\n const appBinaryPathMatch = buildOutput.match(\n /(\\/(?:\\\\\\s|[^ ])+\\/Developer\\/Xcode\\/DerivedData\\/(?:\\\\\\s|[^ ])+\\/Build\\/Products\\/(?:Debug|Release)-(?:[^\\s/]+)\\/(?:\\\\\\s|[^ ])+\\.app)/\n );\n const pathFiltered = appBinaryPathMatch?.filter((a) => typeof a === 'string' && a);\n if (!pathFiltered?.length) {\n throw new CommandError(\n 'XCODE_BUILD',\n `Malformed xcodebuild results: app binary path was not generated in build output. Report this issue and run your project with Xcode instead.`\n );\n } else {\n // Sort for the shortest\n const shortestPath = pathFiltered\n .sort((a: string, b: string) => a.length - b.length)[0]\n ?.trim();\n Log.debug(`Found app binary path: ${shortestPath}`);\n return shortestPath ?? null;\n }\n}\n/**\n *\n * @returns '/Users/evanbacon/Library/Developer/Xcode/DerivedData/myapp-gpgjqjodrxtervaufttwnsgimhrx/Build/Products/Debug-iphonesimulator/myapp.app'\n */\nexport function getAppBinaryPath(buildOutput: string) {\n // Matches what's used in \"Bundle React Native code and images\" script.\n // Requires that `-hideShellScriptEnvironment` is not included in the build command (extra logs).\n\n try {\n // Like `\\=/Users/evanbacon/Library/Developer/Xcode/DerivedData/Exponent-anpuosnglkxokahjhfszejloqfvo/Build/Products/Debug-iphonesimulator`\n const CONFIGURATION_BUILD_DIR = extractEnvVariableFromBuild(\n buildOutput,\n 'CONFIGURATION_BUILD_DIR'\n ).sort(\n // Longer name means more suffixes, we want the shortest possible one to be first.\n // Massive projects (like Expo Go) can sometimes print multiple different sets of environment variables.\n // This can become an issue with some\n (a, b) => a.length - b.length\n );\n // Like `Exponent.app`\n const UNLOCALIZED_RESOURCES_FOLDER_PATH = extractEnvVariableFromBuild(\n buildOutput,\n 'UNLOCALIZED_RESOURCES_FOLDER_PATH'\n );\n\n const binaryPath = path.join(\n // Use the shortest defined env variable (usually there's just one).\n CONFIGURATION_BUILD_DIR[0]!,\n // Use the last defined env variable.\n UNLOCALIZED_RESOURCES_FOLDER_PATH[UNLOCALIZED_RESOURCES_FOLDER_PATH.length - 1]!\n );\n\n // If the app has a space in the name it'll fail because it isn't escaped properly by Xcode.\n return getEscapedPath(binaryPath);\n } catch (error) {\n if (error instanceof CommandError && error.code === 'XCODE_BUILD') {\n const possiblePath = matchEstimatedBinaryPath(buildOutput);\n if (possiblePath) {\n return possiblePath;\n }\n }\n throw error;\n }\n}\n\nexport function getEscapedPath(filePath: string): string {\n if (fs.existsSync(filePath)) {\n return filePath;\n }\n const unescapedPath = filePath.split(/\\\\ /).join(' ');\n if (fs.existsSync(unescapedPath)) {\n return unescapedPath;\n }\n throw new CommandError(\n 'XCODE_BUILD',\n `Unexpected: Generated app at path \"${filePath}\" cannot be read, the app cannot be installed. Report this and build onto a simulator.`\n );\n}\n\nexport function extractEnvVariableFromBuild(buildOutput: string, variableName: string) {\n // Xcode can sometimes escape `=` with a backslash or put the value in quotes\n const reg = new RegExp(`export ${variableName}\\\\\\\\?=(.*)$`, 'mg');\n const matched = [...buildOutput.matchAll(reg)]\n .map((value) => value[1])\n .filter((value): value is string => !!value);\n if (!matched || !matched.length) {\n throw new CommandError(\n 'XCODE_BUILD',\n `Malformed xcodebuild results: \"${variableName}\" variable was not generated in build output. Report this issue and run your project with Xcode instead.`\n );\n }\n return matched;\n}\n\nexport function getProcessOptions({\n packager,\n shouldSkipInitialBundling,\n terminal,\n port,\n eagerBundleOptions,\n}: {\n packager: boolean;\n shouldSkipInitialBundling?: boolean;\n terminal: string | undefined;\n port: number;\n eagerBundleOptions?: string;\n}): SpawnOptionsWithoutStdio {\n const SKIP_BUNDLING = shouldSkipInitialBundling ? '1' : undefined;\n if (packager) {\n return {\n env: {\n ...process.env,\n RCT_TERMINAL: terminal,\n SKIP_BUNDLING,\n RCT_METRO_PORT: port.toString(),\n __EXPO_EAGER_BUNDLE_OPTIONS: eagerBundleOptions,\n },\n };\n }\n\n return {\n env: {\n ...process.env,\n RCT_TERMINAL: terminal,\n SKIP_BUNDLING,\n __EXPO_EAGER_BUNDLE_OPTIONS: eagerBundleOptions,\n // Always skip launching the packager from a build script.\n // The script is used for people building their project directly from Xcode.\n // This essentially means \"› Running script 'Start Packager'\" does nothing.\n RCT_NO_LAUNCH_PACKAGER: 'true',\n // FORCE_BUNDLING: '0'\n },\n };\n}\n\nexport async function getXcodeBuildArgsAsync(\n props: Pick<\n BuildProps,\n | 'buildCache'\n | 'projectRoot'\n | 'xcodeProject'\n | 'configuration'\n | 'scheme'\n | 'device'\n | 'osType'\n | 'isSimulator'\n >\n): Promise<string[]> {\n // Use specific device UDID when available, otherwise use generic simulator destination\n // for build-only workflows (e.g., --device generic).\n const destination = props.device\n ? `id=${props.device.udid}`\n : getGenericSimulatorDestination(props.osType);\n\n const args = [\n props.xcodeProject.isWorkspace ? '-workspace' : '-project',\n props.xcodeProject.name,\n '-configuration',\n props.configuration,\n '-scheme',\n props.scheme,\n '-destination',\n destination,\n\n // Enable parallel code signing for CocoaPods frameworks to speed up device builds.\n // When building for device, multiple frameworks need to be code signed. By default this\n // happens sequentially. This flag allows them to run in parallel.\n // https://github.com/CocoaPods/CocoaPods/pull/6088\n 'COCOAPODS_PARALLEL_CODE_SIGN=true',\n\n // Disable the Xcode compiler index store during CLI builds.\n // The index store is used for code completion, refactoring, and navigation in Xcode IDE.\n // Since CLI builds don't need these features, disabling it saves build time and disk I/O.\n 'COMPILER_INDEX_STORE_ENABLE=NO',\n ];\n\n // Skip code signing setup for generic simulator builds (no device).\n if (\n props.device &&\n (!props.isSimulator || simulatorBuildRequiresCodeSigning(props.projectRoot))\n ) {\n const developmentTeamId = await ensureDeviceIsCodeSignedForDeploymentAsync(props.projectRoot);\n if (developmentTeamId) {\n args.push(\n `DEVELOPMENT_TEAM=${developmentTeamId}`,\n '-allowProvisioningUpdates',\n '-allowProvisioningDeviceRegistration'\n );\n }\n }\n\n // Add last\n if (props.buildCache === false) {\n args.push(\n // Will first clean the derived data folder.\n 'clean',\n // Then build step must be added otherwise the process will simply clean and exit.\n 'build'\n );\n }\n\n if (env.EXPO_PROFILE) {\n args.push('-showBuildTimingSummary');\n }\n\n return args;\n}\n\nfunction spawnXcodeBuild(\n args: string[],\n options: SpawnOptionsWithoutStdio,\n { onData }: { onData: (data: string) => void }\n): Promise<{ code: number | null; results: string; error: string }> {\n const buildProcess = spawn('xcodebuild', args, options);\n\n let results = '';\n let error = '';\n\n buildProcess.stdout.on('data', (data: Buffer) => {\n const stringData = data.toString();\n results += stringData;\n onData(stringData);\n });\n\n buildProcess.stderr.on('data', (data: Buffer) => {\n const stringData = data instanceof Buffer ? data.toString() : data;\n error += stringData;\n });\n\n return new Promise(async (resolve, reject) => {\n buildProcess.on('close', (code: number) => {\n resolve({ code, results, error });\n });\n });\n}\n\nasync function spawnXcodeBuildWithFlush(\n args: string[],\n options: SpawnOptionsWithoutStdio,\n { onFlush }: { onFlush: (data: string) => void }\n): Promise<{ code: number | null; results: string; error: string }> {\n let currentBuffer = '';\n\n // Data can be sent in chunks that would have no relevance to our regex\n // this can cause massive slowdowns, so we need to ensure the data is complete before attempting to parse it.\n function flushBuffer() {\n if (!currentBuffer) {\n return;\n }\n\n const data = currentBuffer;\n // Reset buffer.\n currentBuffer = '';\n // Process data.\n onFlush(data);\n }\n\n const data = await spawnXcodeBuild(args, options, {\n onData(stringData) {\n currentBuffer += stringData;\n // Only flush the data if we have a full line.\n if (currentBuffer.endsWith(os.EOL)) {\n flushBuffer();\n }\n },\n });\n\n // Flush log data at the end just in case we missed something.\n flushBuffer();\n return data;\n}\n\nasync function spawnXcodeBuildWithFormat(\n args: string[],\n options: SpawnOptionsWithoutStdio,\n { projectRoot, xcodeProject }: { projectRoot: string; xcodeProject: ProjectInfo }\n): Promise<{ code: number | null; results: string; error: string; formatter: ExpoRunFormatter }> {\n Log.debug(` xcodebuild ${args.join(' ')}`);\n\n logPrettyItem(chalk.bold`Planning build`);\n\n const formatter = ExpoRunFormatter.create(projectRoot, {\n xcodeProject,\n isDebug: env.EXPO_DEBUG,\n });\n\n const results = await spawnXcodeBuildWithFlush(args, options, {\n onFlush(data) {\n // Process data through formatter for display\n for (const line of formatter.pipe(data)) {\n Log.log(line);\n }\n },\n });\n\n Log.debug(`Exited with code: ${results.code}`);\n\n if (\n // User cancelled with ctrl-c\n results.code === null ||\n // Build interrupted\n results.code === 75\n ) {\n throw new AbortCommandError();\n }\n\n Log.log(formatter.getBuildSummary());\n\n return { ...results, formatter };\n}\n\nexport async function buildAsync(props: BuildProps): Promise<string> {\n const args = await getXcodeBuildArgsAsync(props);\n\n const { projectRoot, xcodeProject, shouldSkipInitialBundling, port, eagerBundleOptions } = props;\n\n // Remove extended attributes that can cause code signing failures before building.\n // These are added by Finder, cloud storage services, or when downloading files.\n await removeExtendedAttributesAsync(projectRoot);\n\n const processOptions = getProcessOptions({\n packager: false,\n terminal: getUserTerminal(),\n shouldSkipInitialBundling,\n port,\n eagerBundleOptions,\n });\n\n // Retry logic for concurrent build failures.\n // When multiple Xcode builds run simultaneously (e.g., in CI), the build database\n // can become locked. We retry with exponential backoff to handle this.\n const maxRetries = 3;\n let retryDelaySeconds = 1;\n let lastResults: {\n code: number | null;\n results: string;\n error: string;\n formatter: ExpoRunFormatter;\n } | null = null;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const { code, results, formatter, error } = await spawnXcodeBuildWithFormat(\n args,\n processOptions,\n { projectRoot, xcodeProject }\n );\n\n lastResults = { code, results, error, formatter };\n\n // If build succeeded or failed for a reason other than concurrent builds, stop retrying\n if (code === 0 || !isConcurrentBuildError(results)) {\n break;\n }\n\n // If we have retries left, wait and try again\n if (attempt < maxRetries) {\n Log.warn(\n `Xcode build failed due to concurrent builds, retrying in ${retryDelaySeconds}s... (attempt ${attempt + 1}/${maxRetries})`\n );\n await new Promise((resolve) => setTimeout(resolve, retryDelaySeconds * 1000));\n retryDelaySeconds *= 2; // Exponential backoff\n } else {\n Log.warn('Xcode build failed due to concurrent builds after maximum retries.');\n }\n }\n\n const { code, results, formatter, error } = lastResults!;\n const logFilePath = writeBuildLogs(projectRoot, results, error);\n\n if (code !== 0) {\n // Determine if the logger found any errors;\n const wasErrorPresented = !!formatter.errors.length;\n\n if (wasErrorPresented) {\n // This has a flaw, if the user is missing a file, and there is a script error, only the missing file error will be shown.\n // They will only see the script error if they fix the missing file and rerun.\n // The flaw can be fixed by catching script errors in the custom logger.\n throw new CommandError(\n `Failed to build iOS project. \"xcodebuild\" exited with error code ${code}.`\n );\n }\n\n _assertXcodeBuildResults(code, results, error, xcodeProject, logFilePath);\n }\n return results;\n}\n\n// Exposed for testing.\nexport function _assertXcodeBuildResults(\n code: number | null,\n results: string,\n error: string,\n xcodeProject: { name: string },\n logFilePath: string\n): void {\n const errorTitle = `Failed to build iOS project. \"xcodebuild\" exited with error code ${code}.`;\n\n const throwWithMessage = (message: string): never => {\n throw new CommandError(\n `${errorTitle}\\nTo view more error logs, try building the app with Xcode directly, by opening ${xcodeProject.name}.\\n\\n` +\n message +\n `Build logs written to ${chalk.underline(logFilePath)}`\n );\n };\n\n const localizedError = error.match(/NSLocalizedFailure = \"(.*)\"/)?.[1];\n\n if (localizedError) {\n throwWithMessage(chalk.bold(localizedError) + '\\n\\n');\n }\n // Show all the log info because often times the error is coming from a shell script,\n // that invoked a node script, that started metro, which threw an error.\n\n throwWithMessage(results + '\\n\\n' + error);\n}\n\nfunction writeBuildLogs(projectRoot: string, buildOutput: string, errorOutput: string) {\n const [logFilePath, errorFilePath] = getErrorLogFilePath(projectRoot);\n\n fs.writeFileSync(logFilePath, buildOutput);\n fs.writeFileSync(errorFilePath, errorOutput);\n return logFilePath;\n}\n\nfunction getErrorLogFilePath(projectRoot: string): [string, string] {\n const folder = path.join(projectRoot, '.expo');\n ensureDirectory(folder);\n return [path.join(folder, 'xcodebuild.log'), path.join(folder, 'xcodebuild-error.log')];\n}\n\n/**\n * Remove extended attributes that can cause code signing failures.\n *\n * Attributes like `com.apple.FinderInfo` and `com.apple.provenance` are added by Finder,\n * cloud storage services (OneDrive, iCloud, Dropbox), or when files are downloaded.\n * These must be removed before code signing or the build may fail.\n *\n * @see https://developer.apple.com/library/archive/qa/qa1940/_index.html\n */\nasync function removeExtendedAttributesAsync(projectRoot: string): Promise<void> {\n // These specific attributes are known to cause code signing issues.\n // We preserve com.apple.xcode.CreatedByBuildSystem which Xcode uses to manage build directories.\n const attributesToRemove = ['com.apple.FinderInfo', 'com.apple.provenance'];\n\n const iosProjectPath = path.join(projectRoot, 'ios');\n\n // Only proceed if the ios directory exists\n if (!fs.existsSync(iosProjectPath)) {\n return;\n }\n\n for (const attribute of attributesToRemove) {\n try {\n // -r: recursive, -d: delete attribute\n await spawnAsync('xattr', ['-r', '-d', attribute, iosProjectPath]);\n } catch {\n // Ignore errors - attribute may not exist or directory may be missing.\n // This is expected behavior and not a problem.\n Log.debug(`Failed to remove extended attribute ${attribute} (this is usually fine)`);\n }\n }\n}\n\n/**\n * Check if the build failure is due to concurrent Xcode builds.\n * When multiple builds run simultaneously, Xcode's build database can become locked.\n */\nfunction isConcurrentBuildError(results: string): boolean {\n return (\n results.includes(CONCURRENT_BUILD_ERROR_MESSAGE_1) &&\n results.includes(CONCURRENT_BUILD_ERROR_MESSAGE_2)\n );\n}\n"],"names":["_assertXcodeBuildResults","buildAsync","extractEnvVariableFromBuild","getAppBinaryPath","getEscapedPath","getGenericSimulatorDestination","getProcessOptions","getXcodeBuildArgsAsync","logPrettyItem","matchEstimatedBinaryPath","CONCURRENT_BUILD_ERROR_MESSAGE_1","CONCURRENT_BUILD_ERROR_MESSAGE_2","osType","message","Log","log","chalk","buildOutput","appBinaryPathMatch","match","pathFiltered","filter","a","length","CommandError","shortestPath","sort","b","trim","debug","CONFIGURATION_BUILD_DIR","UNLOCALIZED_RESOURCES_FOLDER_PATH","binaryPath","path","join","error","code","possiblePath","filePath","fs","existsSync","unescapedPath","split","variableName","reg","RegExp","matched","matchAll","map","value","packager","shouldSkipInitialBundling","terminal","port","eagerBundleOptions","SKIP_BUNDLING","undefined","env","process","RCT_TERMINAL","RCT_METRO_PORT","toString","__EXPO_EAGER_BUNDLE_OPTIONS","RCT_NO_LAUNCH_PACKAGER","props","destination","device","udid","args","xcodeProject","isWorkspace","name","configuration","scheme","isSimulator","simulatorBuildRequiresCodeSigning","projectRoot","developmentTeamId","ensureDeviceIsCodeSignedForDeploymentAsync","push","buildCache","EXPO_PROFILE","spawnXcodeBuild","options","onData","buildProcess","spawn","results","stdout","on","data","stringData","stderr","Buffer","Promise","resolve","reject","spawnXcodeBuildWithFlush","onFlush","currentBuffer","flushBuffer","endsWith","os","EOL","spawnXcodeBuildWithFormat","bold","formatter","ExpoRunFormatter","create","isDebug","EXPO_DEBUG","line","pipe","AbortCommandError","getBuildSummary","removeExtendedAttributesAsync","processOptions","getUserTerminal","maxRetries","retryDelaySeconds","lastResults","attempt","isConcurrentBuildError","warn","setTimeout","logFilePath","writeBuildLogs","wasErrorPresented","errors","errorTitle","throwWithMessage","underline","localizedError","errorOutput","errorFilePath","getErrorLogFilePath","writeFileSync","folder","ensureDirectory","attributesToRemove","iosProjectPath","attribute","spawnAsync","includes"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/run/ios/XcodeBuild.ts"],"sourcesContent":["import spawnAsync from '@expo/spawn-async';\nimport { ExpoRunFormatter } from '@expo/xcpretty';\nimport chalk from 'chalk';\nimport type { SpawnOptionsWithoutStdio } from 'child_process';\nimport { spawn } from 'child_process';\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\n\nimport type { BuildProps, ProjectInfo } from './XcodeBuild.types';\nimport { ensureDeviceIsCodeSignedForDeploymentAsync } from './codeSigning/configureCodeSigning';\nimport { simulatorBuildRequiresCodeSigning } from './codeSigning/simulatorCodeSigning';\nimport * as Log from '../../log';\nimport type { OSType } from '../../start/platforms/ios/simctl';\nimport { ensureDirectory } from '../../utils/dir';\nimport { env } from '../../utils/env';\nimport { AbortCommandError, CommandError } from '../../utils/errors';\nimport { getUserTerminal } from '../../utils/terminal';\n\n// Error messages that indicate concurrent Xcode build failures.\n// When multiple builds run simultaneously, Xcode's build database can become locked.\nconst CONCURRENT_BUILD_ERROR_MESSAGE_1 = 'database is locked';\nconst CONCURRENT_BUILD_ERROR_MESSAGE_2 = 'there are two concurrent builds running';\n\n/** Get the generic Xcode destination string for a given OS type.\n * Used when building without targeting a specific device (build-only workflow).\n */\nexport function getGenericSimulatorDestination(osType: OSType): string {\n switch (osType) {\n case 'tvOS':\n return 'generic/platform=tvOS Simulator';\n case 'watchOS':\n return 'generic/platform=watchOS Simulator';\n case 'xrOS':\n return 'generic/platform=visionOS Simulator';\n case 'iOS':\n default:\n return 'generic/platform=iOS Simulator';\n }\n}\nexport function logPrettyItem(message: string) {\n Log.log(chalk`{whiteBright \\u203A} ${message}`);\n}\n\nexport function matchEstimatedBinaryPath(buildOutput: string): string | null {\n // Match the full path that contains `/(.*)/Developer/Xcode/DerivedData/(.*)/Build/Products/(.*)/(.*).app`\n const appBinaryPathMatch = buildOutput.match(\n /(\\/(?:\\\\\\s|[^ ])+\\/Developer\\/Xcode\\/DerivedData\\/(?:\\\\\\s|[^ ])+\\/Build\\/Products\\/(?:Debug|Release)-(?:[^\\s/]+)\\/(?:\\\\\\s|[^ ])+\\.app)/\n );\n const pathFiltered = appBinaryPathMatch?.filter((a) => typeof a === 'string' && a);\n if (!pathFiltered?.length) {\n throw new CommandError(\n 'XCODE_BUILD',\n `Malformed xcodebuild results: app binary path was not generated in build output. Report this issue and run your project with Xcode instead.`\n );\n } else {\n // Sort for the shortest\n const shortestPath = pathFiltered\n .sort((a: string, b: string) => a.length - b.length)[0]\n ?.trim();\n Log.debug(`Found app binary path: ${shortestPath}`);\n return shortestPath ?? null;\n }\n}\n/**\n *\n * @returns '/Users/evanbacon/Library/Developer/Xcode/DerivedData/myapp-gpgjqjodrxtervaufttwnsgimhrx/Build/Products/Debug-iphonesimulator/myapp.app'\n */\nexport function getAppBinaryPath(buildOutput: string) {\n // Matches what's used in \"Bundle React Native code and images\" script.\n // Requires that `-hideShellScriptEnvironment` is not included in the build command (extra logs).\n\n try {\n // Like `\\=/Users/evanbacon/Library/Developer/Xcode/DerivedData/Exponent-anpuosnglkxokahjhfszejloqfvo/Build/Products/Debug-iphonesimulator`\n const CONFIGURATION_BUILD_DIR = extractEnvVariableFromBuild(\n buildOutput,\n 'CONFIGURATION_BUILD_DIR'\n ).sort(\n // Longer name means more suffixes, we want the shortest possible one to be first.\n // Massive projects (like Expo Go) can sometimes print multiple different sets of environment variables.\n // This can become an issue with some\n (a, b) => a.length - b.length\n );\n // Like `Exponent.app`\n const UNLOCALIZED_RESOURCES_FOLDER_PATH = extractEnvVariableFromBuild(\n buildOutput,\n 'UNLOCALIZED_RESOURCES_FOLDER_PATH'\n );\n\n const binaryPath = path.join(\n // Use the shortest defined env variable (usually there's just one).\n CONFIGURATION_BUILD_DIR[0]!,\n // Use the last defined env variable.\n UNLOCALIZED_RESOURCES_FOLDER_PATH[UNLOCALIZED_RESOURCES_FOLDER_PATH.length - 1]!\n );\n\n // If the app has a space in the name it'll fail because it isn't escaped properly by Xcode.\n return getEscapedPath(binaryPath);\n } catch (error) {\n if (error instanceof CommandError && error.code === 'XCODE_BUILD') {\n const possiblePath = matchEstimatedBinaryPath(buildOutput);\n if (possiblePath) {\n return possiblePath;\n }\n }\n throw error;\n }\n}\n\nexport function getEscapedPath(filePath: string): string {\n if (fs.existsSync(filePath)) {\n return filePath;\n }\n const unescapedPath = filePath.split(/\\\\ /).join(' ');\n if (fs.existsSync(unescapedPath)) {\n return unescapedPath;\n }\n throw new CommandError(\n 'XCODE_BUILD',\n `Unexpected: Generated app at path \"${filePath}\" cannot be read, the app cannot be installed. Report this and build onto a simulator.`\n );\n}\n\nexport function extractEnvVariableFromBuild(buildOutput: string, variableName: string) {\n // Xcode can sometimes escape `=` with a backslash or put the value in quotes\n const reg = new RegExp(`export ${variableName}\\\\\\\\?=(.*)$`, 'mg');\n const matched = [...buildOutput.matchAll(reg)]\n .map((value) => value[1])\n .filter((value): value is string => !!value);\n if (!matched || !matched.length) {\n throw new CommandError(\n 'XCODE_BUILD',\n `Malformed xcodebuild results: \"${variableName}\" variable was not generated in build output. Report this issue and run your project with Xcode instead.`\n );\n }\n return matched;\n}\n\nexport function getProcessOptions({\n packager,\n shouldSkipInitialBundling,\n terminal,\n port,\n eagerBundleOptions,\n}: {\n packager: boolean;\n shouldSkipInitialBundling?: boolean;\n terminal: string | undefined;\n port: number;\n eagerBundleOptions?: string;\n}): SpawnOptionsWithoutStdio {\n const SKIP_BUNDLING = shouldSkipInitialBundling ? '1' : undefined;\n if (packager) {\n return {\n env: {\n ...process.env,\n RCT_TERMINAL: terminal,\n SKIP_BUNDLING,\n RCT_METRO_PORT: port.toString(),\n __EXPO_EAGER_BUNDLE_OPTIONS: eagerBundleOptions,\n },\n };\n }\n\n return {\n env: {\n ...process.env,\n RCT_TERMINAL: terminal,\n SKIP_BUNDLING,\n __EXPO_EAGER_BUNDLE_OPTIONS: eagerBundleOptions,\n // Always skip launching the packager from a build script.\n // The script is used for people building their project directly from Xcode.\n // This essentially means \"› Running script 'Start Packager'\" does nothing.\n RCT_NO_LAUNCH_PACKAGER: 'true',\n // FORCE_BUNDLING: '0'\n },\n };\n}\n\nexport async function getXcodeBuildArgsAsync(\n props: Pick<\n BuildProps,\n | 'buildCache'\n | 'projectRoot'\n | 'xcodeProject'\n | 'configuration'\n | 'scheme'\n | 'device'\n | 'osType'\n | 'isSimulator'\n >\n): Promise<string[]> {\n // Use specific device UDID when available, otherwise use generic simulator destination\n // for build-only workflows (e.g., --device generic).\n const destination = props.device\n ? `id=${props.device.udid}`\n : getGenericSimulatorDestination(props.osType);\n\n const args = [\n props.xcodeProject.isWorkspace ? '-workspace' : '-project',\n props.xcodeProject.name,\n '-configuration',\n props.configuration,\n '-scheme',\n props.scheme,\n '-destination',\n destination,\n\n // Enable parallel code signing for CocoaPods frameworks to speed up device builds.\n // When building for device, multiple frameworks need to be code signed. By default this\n // happens sequentially. This flag allows them to run in parallel.\n // https://github.com/CocoaPods/CocoaPods/pull/6088\n 'COCOAPODS_PARALLEL_CODE_SIGN=true',\n\n // Disable the Xcode compiler index store during CLI builds.\n // The index store is used for code completion, refactoring, and navigation in Xcode IDE.\n // Since CLI builds don't need these features, disabling it saves build time and disk I/O.\n 'COMPILER_INDEX_STORE_ENABLE=NO',\n ];\n\n // Skip code signing setup for generic simulator builds (no device).\n if (\n props.device &&\n (!props.isSimulator || simulatorBuildRequiresCodeSigning(props.projectRoot))\n ) {\n const developmentTeamId = await ensureDeviceIsCodeSignedForDeploymentAsync(props.projectRoot);\n if (developmentTeamId) {\n args.push(\n `DEVELOPMENT_TEAM=${developmentTeamId}`,\n '-allowProvisioningUpdates',\n '-allowProvisioningDeviceRegistration'\n );\n }\n }\n\n // Add last\n if (props.buildCache === false) {\n args.push(\n // Will first clean the derived data folder.\n 'clean',\n // Then build step must be added otherwise the process will simply clean and exit.\n 'build'\n );\n }\n\n if (env.EXPO_PROFILE) {\n args.push('-showBuildTimingSummary');\n }\n\n return args;\n}\n\nfunction spawnXcodeBuild(\n args: string[],\n options: SpawnOptionsWithoutStdio,\n { onData }: { onData: (data: string) => void }\n): Promise<{ code: number | null; results: string; error: string }> {\n const buildProcess = spawn('xcodebuild', args, options);\n\n let results = '';\n let error = '';\n\n buildProcess.stdout.on('data', (data: Buffer) => {\n const stringData = data.toString();\n results += stringData;\n onData(stringData);\n });\n\n buildProcess.stderr.on('data', (data: Buffer) => {\n const stringData = data instanceof Buffer ? data.toString() : data;\n error += stringData;\n });\n\n return new Promise(async (resolve, reject) => {\n buildProcess.on('close', (code: number) => {\n resolve({ code, results, error });\n });\n });\n}\n\nasync function spawnXcodeBuildWithFlush(\n args: string[],\n options: SpawnOptionsWithoutStdio,\n { onFlush }: { onFlush: (data: string) => void }\n): Promise<{ code: number | null; results: string; error: string }> {\n let currentBuffer = '';\n\n // Data can be sent in chunks that would have no relevance to our regex\n // this can cause massive slowdowns, so we need to ensure the data is complete before attempting to parse it.\n function flushBuffer() {\n if (!currentBuffer) {\n return;\n }\n\n const data = currentBuffer;\n // Reset buffer.\n currentBuffer = '';\n // Process data.\n onFlush(data);\n }\n\n const data = await spawnXcodeBuild(args, options, {\n onData(stringData) {\n currentBuffer += stringData;\n // Only flush the data if we have a full line.\n if (currentBuffer.endsWith(os.EOL)) {\n flushBuffer();\n }\n },\n });\n\n // Flush log data at the end just in case we missed something.\n flushBuffer();\n return data;\n}\n\nasync function spawnXcodeBuildWithFormat(\n args: string[],\n options: SpawnOptionsWithoutStdio,\n { projectRoot, xcodeProject }: { projectRoot: string; xcodeProject: ProjectInfo }\n): Promise<{ code: number | null; results: string; error: string; formatter: ExpoRunFormatter }> {\n Log.debug(` xcodebuild ${args.join(' ')}`);\n\n logPrettyItem(chalk.bold`Planning build`);\n\n const formatter = ExpoRunFormatter.create(projectRoot, {\n xcodeProject,\n isDebug: env.EXPO_DEBUG,\n });\n\n const results = await spawnXcodeBuildWithFlush(args, options, {\n onFlush(data) {\n // Process data through formatter for display\n for (const line of formatter.pipe(data)) {\n Log.log(line);\n }\n },\n });\n\n Log.debug(`Exited with code: ${results.code}`);\n\n if (\n // User cancelled with ctrl-c\n results.code === null ||\n // Build interrupted\n results.code === 75\n ) {\n throw new AbortCommandError();\n }\n\n Log.log(formatter.getBuildSummary());\n\n return { ...results, formatter };\n}\n\nexport async function buildAsync(props: BuildProps): Promise<string> {\n const args = await getXcodeBuildArgsAsync(props);\n\n const { projectRoot, xcodeProject, shouldSkipInitialBundling, port, eagerBundleOptions } = props;\n\n // Remove extended attributes that can cause code signing failures before building.\n // These are added by Finder, cloud storage services, or when downloading files.\n await removeExtendedAttributesAsync(projectRoot);\n\n const processOptions = getProcessOptions({\n packager: false,\n terminal: getUserTerminal(),\n shouldSkipInitialBundling,\n port,\n eagerBundleOptions,\n });\n\n // Retry logic for concurrent build failures.\n // When multiple Xcode builds run simultaneously (e.g., in CI), the build database\n // can become locked. We retry with exponential backoff to handle this.\n const maxRetries = 3;\n let retryDelaySeconds = 1;\n let lastResults: {\n code: number | null;\n results: string;\n error: string;\n formatter: ExpoRunFormatter;\n } | null = null;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const { code, results, formatter, error } = await spawnXcodeBuildWithFormat(\n args,\n processOptions,\n { projectRoot, xcodeProject }\n );\n\n lastResults = { code, results, error, formatter };\n\n // If build succeeded or failed for a reason other than concurrent builds, stop retrying\n if (code === 0 || !isConcurrentBuildError(results)) {\n break;\n }\n\n // If we have retries left, wait and try again\n if (attempt < maxRetries) {\n Log.warn(\n `Xcode build failed due to concurrent builds, retrying in ${retryDelaySeconds}s... (attempt ${attempt + 1}/${maxRetries})`\n );\n await new Promise((resolve) => setTimeout(resolve, retryDelaySeconds * 1000));\n retryDelaySeconds *= 2; // Exponential backoff\n } else {\n Log.warn('Xcode build failed due to concurrent builds after maximum retries.');\n }\n }\n\n const { code, results, formatter, error } = lastResults!;\n const logFilePath = writeBuildLogs(projectRoot, results, error);\n\n if (code !== 0) {\n // Determine if the logger found any errors;\n const wasErrorPresented = !!formatter.errors.length;\n\n if (wasErrorPresented) {\n // This has a flaw, if the user is missing a file, and there is a script error, only the missing file error will be shown.\n // They will only see the script error if they fix the missing file and rerun.\n // The flaw can be fixed by catching script errors in the custom logger.\n throw new CommandError(\n `Failed to build iOS project. \"xcodebuild\" exited with error code ${code}.`\n );\n }\n\n _assertXcodeBuildResults(code, results, error, xcodeProject, logFilePath);\n }\n return results;\n}\n\n// Exposed for testing.\nexport function _assertXcodeBuildResults(\n code: number | null,\n results: string,\n error: string,\n xcodeProject: { name: string },\n logFilePath: string\n): void {\n const errorTitle = `Failed to build iOS project. \"xcodebuild\" exited with error code ${code}.`;\n\n const throwWithMessage = (message: string): never => {\n throw new CommandError(\n `${errorTitle}\\nTo view more error logs, try building the app with Xcode directly, by opening ${xcodeProject.name}.\\n\\n` +\n message +\n `Build logs written to ${chalk.underline(logFilePath)}`\n );\n };\n\n const localizedError = error.match(/NSLocalizedFailure = \"(.*)\"/)?.[1];\n\n if (localizedError) {\n throwWithMessage(chalk.bold(localizedError) + '\\n\\n');\n }\n\n // `@expo/xcpretty` can leave a real failure uncounted (its compile-error\n // matching is stateful and anchored, and stderr never passes through it), so\n // the build summary reads \"0 error(s)\" and in CI the full log below is\n // truncated before the real error line.\n const errorLines = _extractXcodeBuildErrorLines(results + '\\n' + error);\n if (errorLines.length) {\n throwWithMessage(chalk.red(errorLines.join('\\n')) + '\\n\\n' + results + '\\n\\n' + error);\n }\n\n // Show all the log info because often times the error is coming from a shell script,\n // that invoked a node script, that started metro, which threw an error.\n\n throwWithMessage(results + '\\n\\n' + error);\n}\n\n// Exposed for testing.\nexport function _extractXcodeBuildErrorLines(output: string): string[] {\n const seen = new Set<string>();\n const errors: string[] = [];\n for (const raw of output.split(/\\r?\\n/)) {\n const line = raw.trim();\n if (/(?:^|\\s)error:\\s/.test(line) && !seen.has(line)) {\n seen.add(line);\n errors.push(line);\n }\n }\n return errors;\n}\n\nfunction writeBuildLogs(projectRoot: string, buildOutput: string, errorOutput: string) {\n const [logFilePath, errorFilePath] = getErrorLogFilePath(projectRoot);\n\n fs.writeFileSync(logFilePath, buildOutput);\n fs.writeFileSync(errorFilePath, errorOutput);\n return logFilePath;\n}\n\nfunction getErrorLogFilePath(projectRoot: string): [string, string] {\n const folder = path.join(projectRoot, '.expo');\n ensureDirectory(folder);\n return [path.join(folder, 'xcodebuild.log'), path.join(folder, 'xcodebuild-error.log')];\n}\n\n/**\n * Remove extended attributes that can cause code signing failures.\n *\n * Attributes like `com.apple.FinderInfo` and `com.apple.provenance` are added by Finder,\n * cloud storage services (OneDrive, iCloud, Dropbox), or when files are downloaded.\n * These must be removed before code signing or the build may fail.\n *\n * @see https://developer.apple.com/library/archive/qa/qa1940/_index.html\n */\nasync function removeExtendedAttributesAsync(projectRoot: string): Promise<void> {\n // These specific attributes are known to cause code signing issues.\n // We preserve com.apple.xcode.CreatedByBuildSystem which Xcode uses to manage build directories.\n const attributesToRemove = ['com.apple.FinderInfo', 'com.apple.provenance'];\n\n const iosProjectPath = path.join(projectRoot, 'ios');\n\n // Only proceed if the ios directory exists\n if (!fs.existsSync(iosProjectPath)) {\n return;\n }\n\n for (const attribute of attributesToRemove) {\n try {\n // -r: recursive, -d: delete attribute\n await spawnAsync('xattr', ['-r', '-d', attribute, iosProjectPath]);\n } catch {\n // Ignore errors - attribute may not exist or directory may be missing.\n // This is expected behavior and not a problem.\n Log.debug(`Failed to remove extended attribute ${attribute} (this is usually fine)`);\n }\n }\n}\n\n/**\n * Check if the build failure is due to concurrent Xcode builds.\n * When multiple builds run simultaneously, Xcode's build database can become locked.\n */\nfunction isConcurrentBuildError(results: string): boolean {\n return (\n results.includes(CONCURRENT_BUILD_ERROR_MESSAGE_1) &&\n results.includes(CONCURRENT_BUILD_ERROR_MESSAGE_2)\n );\n}\n"],"names":["_assertXcodeBuildResults","_extractXcodeBuildErrorLines","buildAsync","extractEnvVariableFromBuild","getAppBinaryPath","getEscapedPath","getGenericSimulatorDestination","getProcessOptions","getXcodeBuildArgsAsync","logPrettyItem","matchEstimatedBinaryPath","CONCURRENT_BUILD_ERROR_MESSAGE_1","CONCURRENT_BUILD_ERROR_MESSAGE_2","osType","message","Log","log","chalk","buildOutput","appBinaryPathMatch","match","pathFiltered","filter","a","length","CommandError","shortestPath","sort","b","trim","debug","CONFIGURATION_BUILD_DIR","UNLOCALIZED_RESOURCES_FOLDER_PATH","binaryPath","path","join","error","code","possiblePath","filePath","fs","existsSync","unescapedPath","split","variableName","reg","RegExp","matched","matchAll","map","value","packager","shouldSkipInitialBundling","terminal","port","eagerBundleOptions","SKIP_BUNDLING","undefined","env","process","RCT_TERMINAL","RCT_METRO_PORT","toString","__EXPO_EAGER_BUNDLE_OPTIONS","RCT_NO_LAUNCH_PACKAGER","props","destination","device","udid","args","xcodeProject","isWorkspace","name","configuration","scheme","isSimulator","simulatorBuildRequiresCodeSigning","projectRoot","developmentTeamId","ensureDeviceIsCodeSignedForDeploymentAsync","push","buildCache","EXPO_PROFILE","spawnXcodeBuild","options","onData","buildProcess","spawn","results","stdout","on","data","stringData","stderr","Buffer","Promise","resolve","reject","spawnXcodeBuildWithFlush","onFlush","currentBuffer","flushBuffer","endsWith","os","EOL","spawnXcodeBuildWithFormat","bold","formatter","ExpoRunFormatter","create","isDebug","EXPO_DEBUG","line","pipe","AbortCommandError","getBuildSummary","removeExtendedAttributesAsync","processOptions","getUserTerminal","maxRetries","retryDelaySeconds","lastResults","attempt","isConcurrentBuildError","warn","setTimeout","logFilePath","writeBuildLogs","wasErrorPresented","errors","errorTitle","throwWithMessage","underline","localizedError","errorLines","red","output","seen","Set","raw","test","has","add","errorOutput","errorFilePath","getErrorLogFilePath","writeFileSync","folder","ensureDirectory","attributesToRemove","iosProjectPath","attribute","spawnAsync","includes"],"mappings":";;;;;;;;;;;QAgbgBA;eAAAA;;QAuCAC;eAAAA;;QApHMC;eAAAA;;QAxONC;eAAAA;;QAvDAC;eAAAA;;QAyCAC;eAAAA;;QAlFAC;eAAAA;;QA+GAC;eAAAA;;QAyCMC;eAAAA;;QA3INC;eAAAA;;QAIAC;eAAAA;;;;gEA5CO;;;;;;;yBACU;;;;;;;gEACf;;;;;;;yBAEI;;;;;;;gEACP;;;;;;;gEACA;;;;;;;gEACE;;;;;;sCAG0C;sCACT;6DAC7B;qBAEW;qBACZ;wBAC4B;0BAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEhC,gEAAgE;AAChE,qFAAqF;AACrF,MAAMC,mCAAmC;AACzC,MAAMC,mCAAmC;AAKlC,SAASN,+BAA+BO,MAAc;IAC3D,OAAQA;QACN,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;QACT,KAAK;QACL;YACE,OAAO;IACX;AACF;AACO,SAASJ,cAAcK,OAAe;IAC3CC,KAAIC,GAAG,CAACC,IAAAA,gBAAK,CAAA,CAAC,qBAAqB,EAAEH,QAAQ,CAAC;AAChD;AAEO,SAASJ,yBAAyBQ,WAAmB;IAC1D,0GAA0G;IAC1G,MAAMC,qBAAqBD,YAAYE,KAAK,CAC1C;IAEF,MAAMC,eAAeF,sCAAAA,mBAAoBG,MAAM,CAAC,CAACC,IAAM,OAAOA,MAAM,YAAYA;IAChF,IAAI,EAACF,gCAAAA,aAAcG,MAAM,GAAE;QACzB,MAAM,IAAIC,oBAAY,CACpB,eACA,CAAC,2IAA2I,CAAC;IAEjJ,OAAO;YAEgBJ;QADrB,wBAAwB;QACxB,MAAMK,gBAAeL,sBAAAA,aAClBM,IAAI,CAAC,CAACJ,GAAWK,IAAcL,EAAEC,MAAM,GAAGI,EAAEJ,MAAM,CAAC,CAAC,EAAE,qBADpCH,oBAEjBQ,IAAI;QACRd,KAAIe,KAAK,CAAC,CAAC,uBAAuB,EAAEJ,cAAc;QAClD,OAAOA,gBAAgB;IACzB;AACF;AAKO,SAAStB,iBAAiBc,WAAmB;IAClD,uEAAuE;IACvE,iGAAiG;IAEjG,IAAI;QACF,2IAA2I;QAC3I,MAAMa,0BAA0B5B,4BAC9Be,aACA,2BACAS,IAAI,CACJ,kFAAkF;QAClF,wGAAwG;QACxG,qCAAqC;QACrC,CAACJ,GAAGK,IAAML,EAAEC,MAAM,GAAGI,EAAEJ,MAAM;QAE/B,sBAAsB;QACtB,MAAMQ,oCAAoC7B,4BACxCe,aACA;QAGF,MAAMe,aAAaC,eAAI,CAACC,IAAI,CAC1B,oEAAoE;QACpEJ,uBAAuB,CAAC,EAAE,EAC1B,qCAAqC;QACrCC,iCAAiC,CAACA,kCAAkCR,MAAM,GAAG,EAAE;QAGjF,4FAA4F;QAC5F,OAAOnB,eAAe4B;IACxB,EAAE,OAAOG,OAAO;QACd,IAAIA,iBAAiBX,oBAAY,IAAIW,MAAMC,IAAI,KAAK,eAAe;YACjE,MAAMC,eAAe5B,yBAAyBQ;YAC9C,IAAIoB,cAAc;gBAChB,OAAOA;YACT;QACF;QACA,MAAMF;IACR;AACF;AAEO,SAAS/B,eAAekC,QAAgB;IAC7C,IAAIC,aAAE,CAACC,UAAU,CAACF,WAAW;QAC3B,OAAOA;IACT;IACA,MAAMG,gBAAgBH,SAASI,KAAK,CAAC,OAAOR,IAAI,CAAC;IACjD,IAAIK,aAAE,CAACC,UAAU,CAACC,gBAAgB;QAChC,OAAOA;IACT;IACA,MAAM,IAAIjB,oBAAY,CACpB,eACA,CAAC,mCAAmC,EAAEc,SAAS,sFAAsF,CAAC;AAE1I;AAEO,SAASpC,4BAA4Be,WAAmB,EAAE0B,YAAoB;IACnF,6EAA6E;IAC7E,MAAMC,MAAM,IAAIC,OAAO,CAAC,OAAO,EAAEF,aAAa,WAAW,CAAC,EAAE;IAC5D,MAAMG,UAAU;WAAI7B,YAAY8B,QAAQ,CAACH;KAAK,CAC3CI,GAAG,CAAC,CAACC,QAAUA,KAAK,CAAC,EAAE,EACvB5B,MAAM,CAAC,CAAC4B,QAA2B,CAAC,CAACA;IACxC,IAAI,CAACH,WAAW,CAACA,QAAQvB,MAAM,EAAE;QAC/B,MAAM,IAAIC,oBAAY,CACpB,eACA,CAAC,+BAA+B,EAAEmB,aAAa,wGAAwG,CAAC;IAE5J;IACA,OAAOG;AACT;AAEO,SAASxC,kBAAkB,EAChC4C,QAAQ,EACRC,yBAAyB,EACzBC,QAAQ,EACRC,IAAI,EACJC,kBAAkB,EAOnB;IACC,MAAMC,gBAAgBJ,4BAA4B,MAAMK;IACxD,IAAIN,UAAU;QACZ,OAAO;YACLO,KAAK;gBACH,GAAGC,QAAQD,GAAG;gBACdE,cAAcP;gBACdG;gBACAK,gBAAgBP,KAAKQ,QAAQ;gBAC7BC,6BAA6BR;YAC/B;QACF;IACF;IAEA,OAAO;QACLG,KAAK;YACH,GAAGC,QAAQD,GAAG;YACdE,cAAcP;YACdG;YACAO,6BAA6BR;YAC7B,0DAA0D;YAC1D,4EAA4E;YAC5E,2EAA2E;YAC3ES,wBAAwB;QAE1B;IACF;AACF;AAEO,eAAexD,uBACpByD,KAUC;IAED,uFAAuF;IACvF,qDAAqD;IACrD,MAAMC,cAAcD,MAAME,MAAM,GAC5B,CAAC,GAAG,EAAEF,MAAME,MAAM,CAACC,IAAI,EAAE,GACzB9D,+BAA+B2D,MAAMpD,MAAM;IAE/C,MAAMwD,OAAO;QACXJ,MAAMK,YAAY,CAACC,WAAW,GAAG,eAAe;QAChDN,MAAMK,YAAY,CAACE,IAAI;QACvB;QACAP,MAAMQ,aAAa;QACnB;QACAR,MAAMS,MAAM;QACZ;QACAR;QAEA,mFAAmF;QACnF,wFAAwF;QACxF,kEAAkE;QAClE,mDAAmD;QACnD;QAEA,4DAA4D;QAC5D,yFAAyF;QACzF,0FAA0F;QAC1F;KACD;IAED,oEAAoE;IACpE,IACED,MAAME,MAAM,IACX,CAAA,CAACF,MAAMU,WAAW,IAAIC,IAAAA,uDAAiC,EAACX,MAAMY,WAAW,CAAA,GAC1E;QACA,MAAMC,oBAAoB,MAAMC,IAAAA,gEAA0C,EAACd,MAAMY,WAAW;QAC5F,IAAIC,mBAAmB;YACrBT,KAAKW,IAAI,CACP,CAAC,iBAAiB,EAAEF,mBAAmB,EACvC,6BACA;QAEJ;IACF;IAEA,WAAW;IACX,IAAIb,MAAMgB,UAAU,KAAK,OAAO;QAC9BZ,KAAKW,IAAI,CACP,4CAA4C;QAC5C,SACA,kFAAkF;QAClF;IAEJ;IAEA,IAAItB,QAAG,CAACwB,YAAY,EAAE;QACpBb,KAAKW,IAAI,CAAC;IACZ;IAEA,OAAOX;AACT;AAEA,SAASc,gBACPd,IAAc,EACde,OAAiC,EACjC,EAAEC,MAAM,EAAsC;IAE9C,MAAMC,eAAeC,IAAAA,sBAAK,EAAC,cAAclB,MAAMe;IAE/C,IAAII,UAAU;IACd,IAAIpD,QAAQ;IAEZkD,aAAaG,MAAM,CAACC,EAAE,CAAC,QAAQ,CAACC;QAC9B,MAAMC,aAAaD,KAAK7B,QAAQ;QAChC0B,WAAWI;QACXP,OAAOO;IACT;IAEAN,aAAaO,MAAM,CAACH,EAAE,CAAC,QAAQ,CAACC;QAC9B,MAAMC,aAAaD,gBAAgBG,SAASH,KAAK7B,QAAQ,KAAK6B;QAC9DvD,SAASwD;IACX;IAEA,OAAO,IAAIG,QAAQ,OAAOC,SAASC;QACjCX,aAAaI,EAAE,CAAC,SAAS,CAACrD;YACxB2D,QAAQ;gBAAE3D;gBAAMmD;gBAASpD;YAAM;QACjC;IACF;AACF;AAEA,eAAe8D,yBACb7B,IAAc,EACde,OAAiC,EACjC,EAAEe,OAAO,EAAuC;IAEhD,IAAIC,gBAAgB;IAEpB,uEAAuE;IACvE,6GAA6G;IAC7G,SAASC;QACP,IAAI,CAACD,eAAe;YAClB;QACF;QAEA,MAAMT,OAAOS;QACb,gBAAgB;QAChBA,gBAAgB;QAChB,gBAAgB;QAChBD,QAAQR;IACV;IAEA,MAAMA,OAAO,MAAMR,gBAAgBd,MAAMe,SAAS;QAChDC,QAAOO,UAAU;YACfQ,iBAAiBR;YACjB,8CAA8C;YAC9C,IAAIQ,cAAcE,QAAQ,CAACC,aAAE,CAACC,GAAG,GAAG;gBAClCH;YACF;QACF;IACF;IAEA,8DAA8D;IAC9DA;IACA,OAAOV;AACT;AAEA,eAAec,0BACbpC,IAAc,EACde,OAAiC,EACjC,EAAEP,WAAW,EAAEP,YAAY,EAAsD;IAEjFvD,KAAIe,KAAK,CAAC,CAAC,aAAa,EAAEuC,KAAKlC,IAAI,CAAC,MAAM;IAE1C1B,cAAcQ,gBAAK,CAACyF,IAAI,CAAC,cAAc,CAAC;IAExC,MAAMC,YAAYC,4BAAgB,CAACC,MAAM,CAAChC,aAAa;QACrDP;QACAwC,SAASpD,QAAG,CAACqD,UAAU;IACzB;IAEA,MAAMvB,UAAU,MAAMU,yBAAyB7B,MAAMe,SAAS;QAC5De,SAAQR,IAAI;YACV,6CAA6C;YAC7C,KAAK,MAAMqB,QAAQL,UAAUM,IAAI,CAACtB,MAAO;gBACvC5E,KAAIC,GAAG,CAACgG;YACV;QACF;IACF;IAEAjG,KAAIe,KAAK,CAAC,CAAC,kBAAkB,EAAE0D,QAAQnD,IAAI,EAAE;IAE7C,IACE,6BAA6B;IAC7BmD,QAAQnD,IAAI,KAAK,QACjB,oBAAoB;IACpBmD,QAAQnD,IAAI,KAAK,IACjB;QACA,MAAM,IAAI6E,yBAAiB;IAC7B;IAEAnG,KAAIC,GAAG,CAAC2F,UAAUQ,eAAe;IAEjC,OAAO;QAAE,GAAG3B,OAAO;QAAEmB;IAAU;AACjC;AAEO,eAAezG,WAAW+D,KAAiB;IAChD,MAAMI,OAAO,MAAM7D,uBAAuByD;IAE1C,MAAM,EAAEY,WAAW,EAAEP,YAAY,EAAElB,yBAAyB,EAAEE,IAAI,EAAEC,kBAAkB,EAAE,GAAGU;IAE3F,mFAAmF;IACnF,gFAAgF;IAChF,MAAMmD,8BAA8BvC;IAEpC,MAAMwC,iBAAiB9G,kBAAkB;QACvC4C,UAAU;QACVE,UAAUiE,IAAAA,yBAAe;QACzBlE;QACAE;QACAC;IACF;IAEA,6CAA6C;IAC7C,kFAAkF;IAClF,uEAAuE;IACvE,MAAMgE,aAAa;IACnB,IAAIC,oBAAoB;IACxB,IAAIC,cAKO;IAEX,IAAK,IAAIC,UAAU,GAAGA,WAAWH,YAAYG,UAAW;QACtD,MAAM,EAAErF,IAAI,EAAEmD,OAAO,EAAEmB,SAAS,EAAEvE,KAAK,EAAE,GAAG,MAAMqE,0BAChDpC,MACAgD,gBACA;YAAExC;YAAaP;QAAa;QAG9BmD,cAAc;YAAEpF;YAAMmD;YAASpD;YAAOuE;QAAU;QAEhD,wFAAwF;QACxF,IAAItE,SAAS,KAAK,CAACsF,uBAAuBnC,UAAU;YAClD;QACF;QAEA,8CAA8C;QAC9C,IAAIkC,UAAUH,YAAY;YACxBxG,KAAI6G,IAAI,CACN,CAAC,yDAAyD,EAAEJ,kBAAkB,cAAc,EAAEE,UAAU,EAAE,CAAC,EAAEH,WAAW,CAAC,CAAC;YAE5H,MAAM,IAAIxB,QAAQ,CAACC,UAAY6B,WAAW7B,SAASwB,oBAAoB;YACvEA,qBAAqB,GAAG,sBAAsB;QAChD,OAAO;YACLzG,KAAI6G,IAAI,CAAC;QACX;IACF;IAEA,MAAM,EAAEvF,IAAI,EAAEmD,OAAO,EAAEmB,SAAS,EAAEvE,KAAK,EAAE,GAAGqF;IAC5C,MAAMK,cAAcC,eAAelD,aAAaW,SAASpD;IAEzD,IAAIC,SAAS,GAAG;QACd,4CAA4C;QAC5C,MAAM2F,oBAAoB,CAAC,CAACrB,UAAUsB,MAAM,CAACzG,MAAM;QAEnD,IAAIwG,mBAAmB;YACrB,0HAA0H;YAC1H,8EAA8E;YAC9E,wEAAwE;YACxE,MAAM,IAAIvG,oBAAY,CACpB,CAAC,iEAAiE,EAAEY,KAAK,CAAC,CAAC;QAE/E;QAEArC,yBAAyBqC,MAAMmD,SAASpD,OAAOkC,cAAcwD;IAC/D;IACA,OAAOtC;AACT;AAGO,SAASxF,yBACdqC,IAAmB,EACnBmD,OAAe,EACfpD,KAAa,EACbkC,YAA8B,EAC9BwD,WAAmB;QAYI1F;IAVvB,MAAM8F,aAAa,CAAC,iEAAiE,EAAE7F,KAAK,CAAC,CAAC;IAE9F,MAAM8F,mBAAmB,CAACrH;QACxB,MAAM,IAAIW,oBAAY,CACpB,GAAGyG,WAAW,gFAAgF,EAAE5D,aAAaE,IAAI,CAAC,KAAK,CAAC,GACtH1D,UACA,CAAC,sBAAsB,EAAEG,gBAAK,CAACmH,SAAS,CAACN,cAAc;IAE7D;IAEA,MAAMO,kBAAiBjG,eAAAA,MAAMhB,KAAK,CAAC,mDAAZgB,YAA4C,CAAC,EAAE;IAEtE,IAAIiG,gBAAgB;QAClBF,iBAAiBlH,gBAAK,CAACyF,IAAI,CAAC2B,kBAAkB;IAChD;IAEA,yEAAyE;IACzE,6EAA6E;IAC7E,uEAAuE;IACvE,wCAAwC;IACxC,MAAMC,aAAarI,6BAA6BuF,UAAU,OAAOpD;IACjE,IAAIkG,WAAW9G,MAAM,EAAE;QACrB2G,iBAAiBlH,gBAAK,CAACsH,GAAG,CAACD,WAAWnG,IAAI,CAAC,SAAS,SAASqD,UAAU,SAASpD;IAClF;IAEA,qFAAqF;IACrF,wEAAwE;IAExE+F,iBAAiB3C,UAAU,SAASpD;AACtC;AAGO,SAASnC,6BAA6BuI,MAAc;IACzD,MAAMC,OAAO,IAAIC;IACjB,MAAMT,SAAmB,EAAE;IAC3B,KAAK,MAAMU,OAAOH,OAAO7F,KAAK,CAAC,SAAU;QACvC,MAAMqE,OAAO2B,IAAI9G,IAAI;QACrB,IAAI,mBAAmB+G,IAAI,CAAC5B,SAAS,CAACyB,KAAKI,GAAG,CAAC7B,OAAO;YACpDyB,KAAKK,GAAG,CAAC9B;YACTiB,OAAOjD,IAAI,CAACgC;QACd;IACF;IACA,OAAOiB;AACT;AAEA,SAASF,eAAelD,WAAmB,EAAE3D,WAAmB,EAAE6H,WAAmB;IACnF,MAAM,CAACjB,aAAakB,cAAc,GAAGC,oBAAoBpE;IAEzDrC,aAAE,CAAC0G,aAAa,CAACpB,aAAa5G;IAC9BsB,aAAE,CAAC0G,aAAa,CAACF,eAAeD;IAChC,OAAOjB;AACT;AAEA,SAASmB,oBAAoBpE,WAAmB;IAC9C,MAAMsE,SAASjH,eAAI,CAACC,IAAI,CAAC0C,aAAa;IACtCuE,IAAAA,oBAAe,EAACD;IAChB,OAAO;QAACjH,eAAI,CAACC,IAAI,CAACgH,QAAQ;QAAmBjH,eAAI,CAACC,IAAI,CAACgH,QAAQ;KAAwB;AACzF;AAEA;;;;;;;;CAQC,GACD,eAAe/B,8BAA8BvC,WAAmB;IAC9D,oEAAoE;IACpE,iGAAiG;IACjG,MAAMwE,qBAAqB;QAAC;QAAwB;KAAuB;IAE3E,MAAMC,iBAAiBpH,eAAI,CAACC,IAAI,CAAC0C,aAAa;IAE9C,2CAA2C;IAC3C,IAAI,CAACrC,aAAE,CAACC,UAAU,CAAC6G,iBAAiB;QAClC;IACF;IAEA,KAAK,MAAMC,aAAaF,mBAAoB;QAC1C,IAAI;YACF,sCAAsC;YACtC,MAAMG,IAAAA,qBAAU,EAAC,SAAS;gBAAC;gBAAM;gBAAMD;gBAAWD;aAAe;QACnE,EAAE,OAAM;YACN,uEAAuE;YACvE,+CAA+C;YAC/CvI,KAAIe,KAAK,CAAC,CAAC,oCAAoC,EAAEyH,UAAU,uBAAuB,CAAC;QACrF;IACF;AACF;AAEA;;;CAGC,GACD,SAAS5B,uBAAuBnC,OAAe;IAC7C,OACEA,QAAQiE,QAAQ,CAAC9I,qCACjB6E,QAAQiE,QAAQ,CAAC7I;AAErB"}
@@ -29,16 +29,16 @@ function _http() {
29
29
  };
30
30
  return data;
31
31
  }
32
- function _http1() {
33
- const data = /*#__PURE__*/ _interop_require_default(require("http"));
34
- _http1 = function() {
32
+ function _nodehttp() {
33
+ const data = /*#__PURE__*/ _interop_require_default(require("node:http"));
34
+ _nodehttp = function() {
35
35
  return data;
36
36
  };
37
37
  return data;
38
38
  }
39
- function _path() {
40
- const data = /*#__PURE__*/ _interop_require_default(require("path"));
41
- _path = function() {
39
+ function _nodepath() {
40
+ const data = /*#__PURE__*/ _interop_require_default(require("node:path"));
41
+ _nodepath = function() {
42
42
  return data;
43
43
  };
44
44
  return data;
@@ -51,6 +51,7 @@ function _send() {
51
51
  return data;
52
52
  }
53
53
  const _log = /*#__PURE__*/ _interop_require_wildcard(require("../log"));
54
+ const _static = require("./static");
54
55
  const _dir = require("../utils/dir");
55
56
  const _errors = require("../utils/errors");
56
57
  const _findUp = require("../utils/findUp");
@@ -115,7 +116,7 @@ async function serveAsync(inputDir, options) {
115
116
  throw new _errors.CommandError('Could not start server. Port is not available.');
116
117
  }
117
118
  options.port = port;
118
- const serverDist = options.isDefaultDirectory ? _path().default.join(inputDir, 'dist') : inputDir;
119
+ const serverDist = options.isDefaultDirectory ? _nodepath().default.join(inputDir, 'dist') : inputDir;
119
120
  // TODO: `.expo/server/ios`, `.expo/server/android`, etc.
120
121
  if (!await (0, _dir.directoryExistsAsync)(serverDist)) {
121
122
  throw new _errors.CommandError(`The server directory ${serverDist} does not exist. Run \`npx expo export\` first.`);
@@ -131,7 +132,8 @@ async function serveAsync(inputDir, options) {
131
132
  // Detect the type of server we need to setup:
132
133
  }
133
134
  async function startStaticServerAsync(dist, options) {
134
- const server = _http1().default.createServer((req, res)=>{
135
+ const staticManifest = await (0, _static.loadStaticManifestAsync)(dist);
136
+ const server = _nodehttp().default.createServer((req, res)=>{
135
137
  var _req_url;
136
138
  // Remove query strings and decode URI
137
139
  const filePath = decodeURI(((_req_url = req.url) == null ? void 0 : _req_url.split('?')[0]) ?? '');
@@ -141,6 +143,16 @@ async function startStaticServerAsync(dist, options) {
141
143
  extensions: [
142
144
  'html'
143
145
  ]
146
+ }).on('headers', (res, resolvedPath)=>{
147
+ // If `headers` doesn't exist in the manifest, do nothing
148
+ if (!(staticManifest == null ? void 0 : staticManifest.headers)) {
149
+ return;
150
+ }
151
+ // Headers only apply to page responses and loader data files, never to other static assets.
152
+ if (!resolvedPath.endsWith('.html') && !filePath.startsWith('/_expo/loaders/')) {
153
+ return;
154
+ }
155
+ (0, _static.applyStaticHeaders)(res, staticManifest.headers);
144
156
  }).on('error', (err)=>{
145
157
  if (err.status === 404) {
146
158
  res.statusCode = 404;
@@ -155,8 +167,8 @@ async function startStaticServerAsync(dist, options) {
155
167
  }
156
168
  async function startDynamicServerAsync(dist, options) {
157
169
  const middleware = (0, _connect().default)();
158
- const staticDirectory = _path().default.join(dist, 'client');
159
- const serverDirectory = _path().default.join(dist, 'server');
170
+ const staticDirectory = _nodepath().default.join(dist, 'client');
171
+ const serverDirectory = _nodepath().default.join(dist, 'server');
160
172
  const serverHandler = (0, _http().createRequestHandler)({
161
173
  build: serverDirectory
162
174
  });
@@ -220,7 +232,7 @@ function canParseURL(url) {
220
232
  }
221
233
  }
222
234
  async function isStaticExportAsync(dist) {
223
- const routesFile = _path().default.join(dist, `server/_expo/routes.json`);
235
+ const routesFile = _nodepath().default.join(dist, `server/_expo/routes.json`);
224
236
  return !await (0, _dir.fileExistsAsync)(routesFile);
225
237
  }
226
238
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/serve/serveAsync.ts"],"sourcesContent":["import chalk from 'chalk';\nimport connect from 'connect';\nimport { createRequestHandler } from 'expo-server/adapter/http';\nimport http from 'http';\nimport path from 'path';\nimport send from 'send';\n\nimport * as Log from '../log';\nimport { directoryExistsAsync, fileExistsAsync } from '../utils/dir';\nimport { CommandError } from '../utils/errors';\nimport { findUpProjectRootOrAssert } from '../utils/findUp';\nimport { setNodeEnv, loadEnvFiles } from '../utils/nodeEnv';\nimport { resolvePortAsync } from '../utils/port';\n\ntype Options = {\n port?: number;\n isDefaultDirectory: boolean;\n};\n\nconst debug = require('debug')('expo:serve') as typeof console.log;\n\n// Start a basic http server\nexport async function serveAsync(inputDir: string, options: Options) {\n const projectRoot = findUpProjectRootOrAssert(inputDir);\n\n setNodeEnv('production');\n loadEnvFiles(projectRoot);\n\n const port = await resolvePortAsync(projectRoot, {\n defaultPort: options.port,\n fallbackPort: 8081,\n });\n\n if (port == null) {\n throw new CommandError('Could not start server. Port is not available.');\n }\n options.port = port;\n\n const serverDist = options.isDefaultDirectory ? path.join(inputDir, 'dist') : inputDir;\n // TODO: `.expo/server/ios`, `.expo/server/android`, etc.\n\n if (!(await directoryExistsAsync(serverDist))) {\n throw new CommandError(\n `The server directory ${serverDist} does not exist. Run \\`npx expo export\\` first.`\n );\n }\n\n const isStatic = await isStaticExportAsync(serverDist);\n\n Log.log(chalk.dim(`Starting ${isStatic ? 'static ' : ''}server in ${serverDist}`));\n\n if (isStatic) {\n await startStaticServerAsync(serverDist, options);\n } else {\n await startDynamicServerAsync(serverDist, options);\n }\n Log.log(`Server running at http://localhost:${options.port}`);\n // Detect the type of server we need to setup:\n}\n\nasync function startStaticServerAsync(dist: string, options: Options) {\n const server = http.createServer((req, res) => {\n // Remove query strings and decode URI\n const filePath = decodeURI(req.url?.split('?')[0] ?? '');\n\n send(req, filePath, {\n root: dist,\n index: 'index.html',\n extensions: ['html'],\n })\n .on('error', (err: any) => {\n if (err.status === 404) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n res.statusCode = err.status || 500;\n res.end('Internal Server Error');\n })\n .pipe(res);\n });\n\n server.listen(options.port!);\n}\n\nasync function startDynamicServerAsync(dist: string, options: Options) {\n const middleware = connect();\n\n const staticDirectory = path.join(dist, 'client');\n const serverDirectory = path.join(dist, 'server');\n\n const serverHandler = createRequestHandler({ build: serverDirectory });\n\n // DOM component CORS support\n middleware.use((req, res, next) => {\n // TODO: Only when origin is `file://` (iOS), and Android equivalent.\n\n // Required for DOM components security in release builds.\n\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader(\n 'Access-Control-Allow-Headers',\n 'Origin, X-Requested-With, Content-Type, Accept, expo-platform'\n );\n\n // Handle OPTIONS preflight requests\n if (req.method === 'OPTIONS') {\n res.statusCode = 200;\n res.end();\n return;\n }\n next();\n });\n\n middleware.use((req, res, next) => {\n if (!req?.url || (req.method !== 'GET' && req.method !== 'HEAD')) {\n return next();\n }\n\n const pathname = canParseURL(req.url) ? new URL(req.url).pathname : req.url;\n if (!pathname) {\n return next();\n }\n\n debug(`Maybe serve static:`, pathname);\n\n const stream = send(req, pathname, {\n root: staticDirectory,\n extensions: ['html'],\n });\n\n // add file listener for fallthrough\n let forwardError = false;\n stream.on('file', function onFile() {\n // once file is determined, always forward error\n forwardError = true;\n });\n\n // forward errors\n stream.on('error', function error(err: any) {\n if (forwardError || !(err.statusCode < 500)) {\n next(err);\n return;\n }\n\n next();\n });\n\n // pipe\n stream.pipe(res);\n });\n\n middleware.use(serverHandler);\n\n middleware.listen(options.port!);\n}\n\nfunction canParseURL(url: string): boolean {\n try {\n // eslint-disable-next-line no-new\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isStaticExportAsync(dist: string): Promise<boolean> {\n const routesFile = path.join(dist, `server/_expo/routes.json`);\n return !(await fileExistsAsync(routesFile));\n}\n"],"names":["serveAsync","debug","require","inputDir","options","projectRoot","findUpProjectRootOrAssert","setNodeEnv","loadEnvFiles","port","resolvePortAsync","defaultPort","fallbackPort","CommandError","serverDist","isDefaultDirectory","path","join","directoryExistsAsync","isStatic","isStaticExportAsync","Log","log","chalk","dim","startStaticServerAsync","startDynamicServerAsync","dist","server","http","createServer","req","res","filePath","decodeURI","url","split","send","root","index","extensions","on","err","status","statusCode","end","pipe","listen","middleware","connect","staticDirectory","serverDirectory","serverHandler","createRequestHandler","build","use","next","setHeader","method","pathname","canParseURL","URL","stream","forwardError","onFile","error","routesFile","fileExistsAsync"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/serve/serveAsync.ts"],"sourcesContent":["import chalk from 'chalk';\nimport connect from 'connect';\nimport { createRequestHandler } from 'expo-server/adapter/http';\nimport http from 'node:http';\nimport path from 'node:path';\nimport send from 'send';\n\nimport * as Log from '../log';\nimport { applyStaticHeaders, loadStaticManifestAsync } from './static';\nimport { directoryExistsAsync, fileExistsAsync } from '../utils/dir';\nimport { CommandError } from '../utils/errors';\nimport { findUpProjectRootOrAssert } from '../utils/findUp';\nimport { setNodeEnv, loadEnvFiles } from '../utils/nodeEnv';\nimport { resolvePortAsync } from '../utils/port';\n\ntype Options = {\n port?: number;\n isDefaultDirectory: boolean;\n};\n\nconst debug = require('debug')('expo:serve') as typeof console.log;\n\n// Start a basic http server\nexport async function serveAsync(inputDir: string, options: Options) {\n const projectRoot = findUpProjectRootOrAssert(inputDir);\n\n setNodeEnv('production');\n loadEnvFiles(projectRoot);\n\n const port = await resolvePortAsync(projectRoot, {\n defaultPort: options.port,\n fallbackPort: 8081,\n });\n\n if (port == null) {\n throw new CommandError('Could not start server. Port is not available.');\n }\n options.port = port;\n\n const serverDist = options.isDefaultDirectory ? path.join(inputDir, 'dist') : inputDir;\n // TODO: `.expo/server/ios`, `.expo/server/android`, etc.\n\n if (!(await directoryExistsAsync(serverDist))) {\n throw new CommandError(\n `The server directory ${serverDist} does not exist. Run \\`npx expo export\\` first.`\n );\n }\n\n const isStatic = await isStaticExportAsync(serverDist);\n\n Log.log(chalk.dim(`Starting ${isStatic ? 'static ' : ''}server in ${serverDist}`));\n\n if (isStatic) {\n await startStaticServerAsync(serverDist, options);\n } else {\n await startDynamicServerAsync(serverDist, options);\n }\n Log.log(`Server running at http://localhost:${options.port}`);\n // Detect the type of server we need to setup:\n}\n\nasync function startStaticServerAsync(dist: string, options: Options) {\n const staticManifest = await loadStaticManifestAsync(dist);\n\n const server = http.createServer((req, res) => {\n // Remove query strings and decode URI\n const filePath = decodeURI(req.url?.split('?')[0] ?? '');\n\n send(req, filePath, {\n root: dist,\n index: 'index.html',\n extensions: ['html'],\n })\n .on('headers', (res: http.ServerResponse, resolvedPath: string) => {\n // If `headers` doesn't exist in the manifest, do nothing\n if (!staticManifest?.headers) {\n return;\n }\n // Headers only apply to page responses and loader data files, never to other static assets.\n if (!resolvedPath.endsWith('.html') && !filePath.startsWith('/_expo/loaders/')) {\n return;\n }\n\n applyStaticHeaders(res, staticManifest.headers);\n })\n .on('error', (err: any) => {\n if (err.status === 404) {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n res.statusCode = err.status || 500;\n res.end('Internal Server Error');\n })\n .pipe(res);\n });\n\n server.listen(options.port!);\n}\n\nasync function startDynamicServerAsync(dist: string, options: Options) {\n const middleware = connect();\n\n const staticDirectory = path.join(dist, 'client');\n const serverDirectory = path.join(dist, 'server');\n\n const serverHandler = createRequestHandler({ build: serverDirectory });\n\n // DOM component CORS support\n middleware.use((req, res, next) => {\n // TODO: Only when origin is `file://` (iOS), and Android equivalent.\n\n // Required for DOM components security in release builds.\n\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader(\n 'Access-Control-Allow-Headers',\n 'Origin, X-Requested-With, Content-Type, Accept, expo-platform'\n );\n\n // Handle OPTIONS preflight requests\n if (req.method === 'OPTIONS') {\n res.statusCode = 200;\n res.end();\n return;\n }\n next();\n });\n\n middleware.use((req, res, next) => {\n if (!req?.url || (req.method !== 'GET' && req.method !== 'HEAD')) {\n return next();\n }\n\n const pathname = canParseURL(req.url) ? new URL(req.url).pathname : req.url;\n if (!pathname) {\n return next();\n }\n\n debug(`Maybe serve static:`, pathname);\n\n const stream = send(req, pathname, {\n root: staticDirectory,\n extensions: ['html'],\n });\n\n // add file listener for fallthrough\n let forwardError = false;\n stream.on('file', function onFile() {\n // once file is determined, always forward error\n forwardError = true;\n });\n\n // forward errors\n stream.on('error', function error(err: any) {\n if (forwardError || !(err.statusCode < 500)) {\n next(err);\n return;\n }\n\n next();\n });\n\n // pipe\n stream.pipe(res);\n });\n\n middleware.use(serverHandler);\n\n middleware.listen(options.port!);\n}\n\nfunction canParseURL(url: string): boolean {\n try {\n // eslint-disable-next-line no-new\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function isStaticExportAsync(dist: string): Promise<boolean> {\n const routesFile = path.join(dist, `server/_expo/routes.json`);\n return !(await fileExistsAsync(routesFile));\n}\n"],"names":["serveAsync","debug","require","inputDir","options","projectRoot","findUpProjectRootOrAssert","setNodeEnv","loadEnvFiles","port","resolvePortAsync","defaultPort","fallbackPort","CommandError","serverDist","isDefaultDirectory","path","join","directoryExistsAsync","isStatic","isStaticExportAsync","Log","log","chalk","dim","startStaticServerAsync","startDynamicServerAsync","dist","staticManifest","loadStaticManifestAsync","server","http","createServer","req","res","filePath","decodeURI","url","split","send","root","index","extensions","on","resolvedPath","headers","endsWith","startsWith","applyStaticHeaders","err","status","statusCode","end","pipe","listen","middleware","connect","staticDirectory","serverDirectory","serverHandler","createRequestHandler","build","use","next","setHeader","method","pathname","canParseURL","URL","stream","forwardError","onFile","error","routesFile","fileExistsAsync"],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: Object.getOwnPropertyDescriptor(all, name).get
9
+ });
10
+ }
11
+ _export(exports, {
12
+ get applyStaticHeaders () {
13
+ return applyStaticHeaders;
14
+ },
15
+ get loadStaticManifestAsync () {
16
+ return loadStaticManifestAsync;
17
+ }
18
+ });
19
+ function _promises() {
20
+ const data = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
21
+ _promises = function() {
22
+ return data;
23
+ };
24
+ return data;
25
+ }
26
+ function _nodepath() {
27
+ const data = /*#__PURE__*/ _interop_require_default(require("node:path"));
28
+ _nodepath = function() {
29
+ return data;
30
+ };
31
+ return data;
32
+ }
33
+ function _interop_require_default(obj) {
34
+ return obj && obj.__esModule ? obj : {
35
+ default: obj
36
+ };
37
+ }
38
+ async function loadStaticManifestAsync(dist) {
39
+ var _json_redirects;
40
+ let json;
41
+ try {
42
+ json = JSON.parse(await _promises().default.readFile(_nodepath().default.join(dist, '_expo/.routes.json'), 'utf8'));
43
+ } catch {
44
+ return null;
45
+ }
46
+ return {
47
+ ...json,
48
+ redirects: (_json_redirects = json.redirects) == null ? void 0 : _json_redirects.map((rule)=>({
49
+ ...rule,
50
+ namedRegex: new RegExp(rule.namedRegex)
51
+ }))
52
+ };
53
+ }
54
+ function applyStaticHeaders(res, headers) {
55
+ for (const [name, value] of Object.entries(headers)){
56
+ if (name.toLowerCase() === 'content-type') {
57
+ continue;
58
+ }
59
+ if (Array.isArray(value)) {
60
+ for (const headerValue of value){
61
+ res.appendHeader(name, headerValue);
62
+ }
63
+ } else if (!res.hasHeader(name)) {
64
+ res.setHeader(name, value);
65
+ }
66
+ }
67
+ }
68
+
69
+ //# sourceMappingURL=static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/serve/static.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport http from 'node:http';\nimport path from 'node:path';\n\nimport type { StaticManifest } from '../export/static';\n\nexport async function loadStaticManifestAsync(\n dist: string\n): Promise<StaticManifest<RegExp> | null> {\n let json: StaticManifest<string>;\n try {\n json = JSON.parse(await fs.readFile(path.join(dist, '_expo/.routes.json'), 'utf8'));\n } catch {\n return null;\n }\n\n return {\n ...json,\n redirects: json.redirects?.map((rule) => ({\n ...rule,\n namedRegex: new RegExp(rule.namedRegex),\n })),\n };\n}\n\n/**\n * Applies headers from a static export manifest to a response, except\n * `Content-Type` which must come from the served file.\n */\nexport function applyStaticHeaders(\n res: http.ServerResponse,\n headers: Record<string, string | string[]>\n): void {\n for (const [name, value] of Object.entries(headers)) {\n if (name.toLowerCase() === 'content-type') {\n continue;\n }\n if (Array.isArray(value)) {\n for (const headerValue of value) {\n res.appendHeader(name, headerValue);\n }\n } else if (!res.hasHeader(name)) {\n res.setHeader(name, value);\n }\n }\n}\n"],"names":["applyStaticHeaders","loadStaticManifestAsync","dist","json","JSON","parse","fs","readFile","path","join","redirects","map","rule","namedRegex","RegExp","res","headers","name","value","Object","entries","toLowerCase","Array","isArray","headerValue","appendHeader","hasHeader","setHeader"],"mappings":";;;;;;;;;;;QA6BgBA;eAAAA;;QAvBMC;eAAAA;;;;gEANP;;;;;;;gEAEE;;;;;;;;;;;AAIV,eAAeA,wBACpBC,IAAY;QAWCC;IATb,IAAIA;IACJ,IAAI;QACFA,OAAOC,KAAKC,KAAK,CAAC,MAAMC,mBAAE,CAACC,QAAQ,CAACC,mBAAI,CAACC,IAAI,CAACP,MAAM,uBAAuB;IAC7E,EAAE,OAAM;QACN,OAAO;IACT;IAEA,OAAO;QACL,GAAGC,IAAI;QACPO,SAAS,GAAEP,kBAAAA,KAAKO,SAAS,qBAAdP,gBAAgBQ,GAAG,CAAC,CAACC,OAAU,CAAA;gBACxC,GAAGA,IAAI;gBACPC,YAAY,IAAIC,OAAOF,KAAKC,UAAU;YACxC,CAAA;IACF;AACF;AAMO,SAASb,mBACde,GAAwB,EACxBC,OAA0C;IAE1C,KAAK,MAAM,CAACC,MAAMC,MAAM,IAAIC,OAAOC,OAAO,CAACJ,SAAU;QACnD,IAAIC,KAAKI,WAAW,OAAO,gBAAgB;YACzC;QACF;QACA,IAAIC,MAAMC,OAAO,CAACL,QAAQ;YACxB,KAAK,MAAMM,eAAeN,MAAO;gBAC/BH,IAAIU,YAAY,CAACR,MAAMO;YACzB;QACF,OAAO,IAAI,CAACT,IAAIW,SAAS,CAACT,OAAO;YAC/BF,IAAIY,SAAS,CAACV,MAAMC;QACtB;IACF;AACF"}
@@ -26,7 +26,7 @@ class FetchClient {
26
26
  this.headers = {
27
27
  accept: 'application/json',
28
28
  'content-type': 'application/json',
29
- 'user-agent': `expo-cli/${"56.1.18"}`,
29
+ 'user-agent': `expo-cli/${"56.1.20"}`,
30
30
  authorization: 'Basic ' + _nodebuffer().Buffer.from(`${target}:`).toString('base64')
31
31
  };
32
32
  }
@@ -83,7 +83,7 @@ function createContext() {
83
83
  cpu: summarizeCpuInfo(),
84
84
  app: {
85
85
  name: 'expo/cli',
86
- version: "56.1.18"
86
+ version: "56.1.20"
87
87
  },
88
88
  ci: _ciinfo().isCI ? {
89
89
  name: _ciinfo().name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/cli",
3
- "version": "56.1.18",
3
+ "version": "56.1.20",
4
4
  "description": "The Expo CLI",
5
5
  "main": "main.js",
6
6
  "bin": {
@@ -39,23 +39,23 @@
39
39
  "homepage": "https://github.com/expo/expo/tree/main/packages/@expo/cli",
40
40
  "dependencies": {
41
41
  "@expo/code-signing-certificates": "^0.0.6",
42
- "@expo/config": "~56.0.11",
43
- "@expo/config-plugins": "~56.0.11",
42
+ "@expo/config": "~56.0.12",
43
+ "@expo/config-plugins": "~56.0.13",
44
44
  "@expo/devcert": "^1.2.1",
45
45
  "@expo/env": "~2.3.1",
46
- "@expo/image-utils": "^0.10.2",
47
- "@expo/inline-modules": "^0.0.13",
46
+ "@expo/image-utils": "^0.10.3",
47
+ "@expo/inline-modules": "^0.0.14",
48
48
  "@expo/json-file": "^10.2.0",
49
49
  "@expo/log-box": "^56.0.14",
50
50
  "@expo/metro": "~56.0.0",
51
- "@expo/metro-config": "~56.0.16",
51
+ "@expo/metro-config": "~56.0.17",
52
52
  "@expo/metro-file-map": "^56.0.3",
53
53
  "@expo/osascript": "^2.6.0",
54
54
  "@expo/package-manager": "^1.12.1",
55
55
  "@expo/plist": "^0.7.0",
56
- "@expo/prebuild-config": "^56.0.18",
57
- "@expo/require-utils": "^56.1.4",
58
- "@expo/router-server": "^56.0.15",
56
+ "@expo/prebuild-config": "^56.0.20",
57
+ "@expo/require-utils": "^56.1.5",
58
+ "@expo/router-server": "^56.0.16",
59
59
  "@expo/schema-utils": "^56.0.2",
60
60
  "@expo/spawn-async": "^1.8.0",
61
61
  "@expo/ws-tunnel": "^2.0.0",
@@ -157,13 +157,13 @@
157
157
  "playwright": "^1.59.0",
158
158
  "taskr": "^1.1.0",
159
159
  "tree-kill": "^1.2.2",
160
+ "@expo/fingerprint": "0.19.8",
161
+ "expo": "56.0.16",
160
162
  "expo-module-scripts": "56.0.3",
161
- "expo": "56.0.14",
162
- "expo-modules-autolinking": "56.0.18",
163
- "@expo/fingerprint": "0.19.6",
164
- "expo-router": "56.2.13"
163
+ "expo-router": "56.2.15",
164
+ "expo-modules-autolinking": "56.0.20"
165
165
  },
166
- "gitHead": "b2e161a54f90a778ab7e5560c0c8f021bbfcaae2",
166
+ "gitHead": "8a480d22f04f94fd36570a319baeaa3529330794",
167
167
  "scripts": {
168
168
  "build": "taskr",
169
169
  "clean": "expo-module clean",