react-native 0.88.0-rc.0 → 0.88.0-rc.2

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.
Files changed (61) hide show
  1. package/Libraries/Core/ReactNativeVersion.js +1 -1
  2. package/Libraries/Renderer/implementations/ReactFabric-dev.js +3056 -1531
  3. package/Libraries/Renderer/implementations/ReactFabric-prod.js +2239 -1011
  4. package/Libraries/Renderer/implementations/ReactFabric-profiling.js +2451 -1124
  5. package/React/Base/RCTVersion.m +1 -1
  6. package/React/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +3 -4
  7. package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +8 -0
  8. package/ReactAndroid/gradle.properties +1 -1
  9. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +7 -1
  10. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +11 -1
  11. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +3 -1
  12. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +3 -1
  13. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +12 -1
  14. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android.kt +3 -1
  15. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +3 -1
  16. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  17. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +15 -1
  18. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +4 -1
  19. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  20. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +5 -1
  21. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +6 -1
  22. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +74 -56
  23. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +4 -2
  24. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +5 -1
  25. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +10 -1
  26. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSCanary.h +5 -1
  27. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +2 -1
  28. package/ReactCommon/react/nativemodule/core/iostests/RCTTurboModuleTests.mm +18 -65
  29. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.h +1 -2
  30. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTInteropTurboModule.mm +1 -2
  31. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h +2 -9
  32. package/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm +33 -71
  33. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +6 -1
  34. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +3 -1
  35. package/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm +8 -19
  36. package/package.json +16 -13
  37. package/scripts/codegen/generate-artifacts-executor/generateRCTThirdPartyComponents.js +18 -4
  38. package/scripts/setup-apple-spm.js +82 -4
  39. package/scripts/spm/autolinking-plugins.js +8 -5
  40. package/scripts/spm/expand-spm-dependencies.js +217 -156
  41. package/scripts/spm/generate-spm-autolinking.js +114 -88
  42. package/scripts/spm/generate-spm-xcodeproj.js +67 -22
  43. package/scripts/spm/ios-deployment-target.js +378 -0
  44. package/scripts/spm/read-podspec.js +156 -8
  45. package/scripts/spm/scaffold-package-swift.js +272 -105
  46. package/scripts/spm/spm-pbxproj.js +36 -21
  47. package/scripts/spm/spm-types.js +25 -8
  48. package/scripts/spm/spm-utils.js +26 -0
  49. package/scripts/spm/swiftpm-config.js +245 -0
  50. package/scripts/spm/sync-spm-autolinking.js +11 -2
  51. package/sdks/hermes-engine/version.properties +1 -1
  52. package/src/private/featureflags/ReactNativeFeatureFlags.js +10 -5
  53. package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +2 -1
  54. package/src/private/webapis/dom/nodes/ReactNativeElement.js +6 -4
  55. package/src/private/webapis/dom/nodes/ReadOnlyText.js +6 -4
  56. package/src/react-private-interface.js +4 -0
  57. package/src/react-private-interface.js.flow +2 -0
  58. package/types_generated/Libraries/ReactNative/FabricUIManager.d.ts +47 -0
  59. package/types_generated/src/react-private-interface.d.ts +4 -1
  60. package/React/Base/RCTArrayBuffer.h +0 -64
  61. package/React/Base/RCTArrayBuffer.mm +0 -127
@@ -10,7 +10,8 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- /*:: import type {
13
+ /*:: import type {SwiftpmConfig} from './swiftpm-config';
14
+ import type {
14
15
  AggregatorInput,
15
16
  AutolinkedDep,
16
17
  AutolinkingArgs,
@@ -62,11 +63,15 @@ const {
62
63
  SpmNameCollisionError,
63
64
  assertSwiftNameNotReserved,
64
65
  defaultReadConfig,
66
+ defaultReadPodspec,
65
67
  defaultResolveDep,
66
68
  expandSpmDependencies,
67
- isValidSwiftName,
68
69
  } = require('./expand-spm-dependencies');
69
- const {readPodspec} = require('./read-podspec');
70
+ const {
71
+ MIN_IOS_VERSION_SUPPORTED,
72
+ sanitizeIosDeploymentTarget,
73
+ } = require('./ios-deployment-target');
74
+ const {findPodspecs, readPodspecCached} = require('./read-podspec');
70
75
  const {
71
76
  AUTOLINKED_PACKAGE_NAME,
72
77
  REACT_CODEGEN_PACKAGE_NAME,
@@ -74,10 +79,14 @@ const {
74
79
  REACT_NATIVE_PACKAGE_NAME,
75
80
  REACT_NATIVE_PRODUCTS,
76
81
  RemoteVersionError,
82
+ displayPath,
77
83
  findProjectRoot,
84
+ isValidSwiftName,
78
85
  makeLogger,
79
86
  remotePackageConfig,
87
+ swiftNameKey,
80
88
  } = require('./spm-utils');
89
+ const {readSwiftpmConfig, stringList} = require('./swiftpm-config');
81
90
  const fs = require('node:fs');
82
91
  const path = require('node:path');
83
92
  const yargs = require('yargs');
@@ -205,6 +214,10 @@ function parseArgs(argv /*: Array<string> */) /*: AutolinkingArgs */ {
205
214
  describe:
206
215
  'Path to the xcframeworks sub-package (absolute or relative to appRoot)',
207
216
  })
217
+ .option('ios-deployment-target', {
218
+ type: 'string',
219
+ describe: `Platform floor of the generated manifests (default: ${MIN_IOS_VERSION_SUPPORTED})`,
220
+ })
208
221
  .usage(
209
222
  'Usage: $0 [options]\n\nGenerates autolinked/Package.swift for SPM autolinking.',
210
223
  )
@@ -217,6 +230,9 @@ function parseArgs(argv /*: Array<string> */) /*: AutolinkingArgs */ {
217
230
  autolinkingJson: parsed['autolinking-json'] ?? null,
218
231
  output: parsed.output ?? null,
219
232
  xcframeworksPath: parsed['xcframeworks-path'] ?? null,
233
+ iosDeploymentTarget: sanitizeIosDeploymentTarget(
234
+ parsed['ios-deployment-target'],
235
+ ),
220
236
  };
221
237
  }
222
238
 
@@ -233,92 +249,116 @@ function readAutolinkingJson(
233
249
  }
234
250
 
235
251
  /**
236
- * Attempts to read react-native.config.js to find spm.modules entries.
237
- * These are extra modules not discoverable via autolinking.json.
252
+ * One of the app's settings, and the root that declared it. Looked up field by
253
+ * field so a project-root `swiftpmConfig` cannot hide a field the Xcode dir
254
+ * still declares: the directory holding the app's package.json wins (where
255
+ * codegen reads `codegenConfig` from), and `appRoot` — the Xcode project dir,
256
+ * the settings' old home — fills the gaps.
257
+ */
258
+ function appConfigField(
259
+ appRoot /*: string */,
260
+ field /*: 'modules' | 'denyPlugins' */,
261
+ ) /*: ?{root: string, value: unknown} */ {
262
+ const projectRoot = findProjectRoot(appRoot);
263
+ const roots = projectRoot === appRoot ? [appRoot] : [projectRoot, appRoot];
264
+ for (const root of roots) {
265
+ const value = readSwiftpmConfig(root, defaultReadConfig(root))?.[field];
266
+ if (value === undefined) {
267
+ continue;
268
+ }
269
+ if (root !== projectRoot) {
270
+ warn(
271
+ `Read '${field}' from ${displayPath(root)}. Move it to 'swiftpmConfig' in ${displayPath(path.join(projectRoot, 'package.json'))} — an app's SwiftPM settings belong with its package.json.`,
272
+ );
273
+ }
274
+ return {root, value};
275
+ }
276
+ return null;
277
+ }
278
+
279
+ /**
280
+ * The app's own native modules — the ones autolinking.json cannot discover —
281
+ * with the root that declared them, since each `path` is relative to the file
282
+ * it is written in:
238
283
  *
239
- * Expected structure in react-native.config.js:
240
- * module.exports = {
241
- * ...
242
- * spm: {
243
- * modules: [
284
+ * // <project root>/package.json
285
+ * "swiftpmConfig": {
286
+ * "modules": [
244
287
  * {
245
- * name: "MyNativeModule",
246
- * path: "ios/MyNativeModule", // relative to appRoot
247
- * exclude: ["*.js", "*.podspec"], // optional
288
+ * "name": "MyNativeModule",
289
+ * "path": "ios/MyNativeModule",
290
+ * "exclude": ["*.js", "*.podspec"]
248
291
  * }
249
292
  * ]
250
293
  * }
251
- * }
252
294
  */
253
295
  function readSpmModulesFromConfig(
254
296
  appRoot /*: string */,
255
- ) /*: Array<SpmModuleConfig> */ {
256
- const configPath = path.join(appRoot, 'react-native.config.js');
257
- if (!fs.existsSync(configPath)) {
258
- return [];
297
+ ) /*: {modules: Array<SpmModuleConfig>, root: string} */ {
298
+ const declared = appConfigField(appRoot, 'modules');
299
+ if (declared == null || !Array.isArray(declared.value)) {
300
+ return {modules: [], root: appRoot};
259
301
  }
260
- try {
261
- // $FlowFixMe[unsupported-syntax] dynamic require by computed path
262
- const config = require(configPath);
263
- return config.spm?.modules ?? [];
264
- } catch (e) {
265
- // Config might use Ruby interop or other patterns – skip
266
- return [];
302
+ // Entries stay unvalidated here: assertSpmModuleName checks each name, and
303
+ // the emission loop reads the rest defensively.
304
+ // $FlowFixMe[incompatible-type] user-authored entries
305
+ const modules /*: ReadonlyArray<SpmModuleConfig> */ = declared.value;
306
+ return {modules: [...modules], root: declared.root};
307
+ }
308
+
309
+ function moduleClashDiagnosis(
310
+ moduleName /*: string */,
311
+ clash /*: string */,
312
+ ) /*: string */ {
313
+ if (clash === moduleName) {
314
+ return `is already the name of another autolinked target.`;
267
315
  }
316
+ if (clash.toLowerCase() === moduleName.toLowerCase()) {
317
+ return `differs from the existing target '${clash}' only in case, which collides on case-insensitive filesystems.`;
318
+ }
319
+ return `compiles as the same module as the existing target '${clash}'.`;
268
320
  }
269
321
 
270
322
  /**
271
323
  * Validates one app-local `spm.modules` name against the same rules a library's
272
324
  * `spm.name` gets: a usable Swift identifier, not a name React Native reserves,
273
325
  * and not one already taken by another module or an autolinked dep.
274
- * `taken` maps lower-cased name → the name as written.
326
+ * `taken` is keyed by swiftNameKey, valued with the name as written.
275
327
  */
276
328
  function assertSpmModuleName(
277
329
  name /*: unknown */,
278
330
  taken /*: Map<string, string> */,
279
331
  ) /*: void */ {
280
332
  const remedy =
281
- "Rename it in this app's react-native.config.js 'spm.modules'.";
333
+ "Rename it in this app's package.json 'swiftpmConfig.modules'.";
282
334
  if (typeof name !== 'string' || !isValidSwiftName(name)) {
283
335
  throw new Error(
284
- `react-native autolinking: invalid 'spm.modules' name ${JSON.stringify(name) ?? 'undefined'}: must start with a letter or underscore and contain only letters, digits, underscores, or hyphens.`,
336
+ `react-native autolinking: invalid 'swiftpmConfig.modules' name ${JSON.stringify(name) ?? 'undefined'}: must start with a letter or underscore and contain only letters, digits, underscores, or hyphens.`,
285
337
  );
286
338
  }
287
339
  const moduleName = name;
288
340
  assertSwiftNameNotReserved(moduleName, {
289
- label: `the 'spm.modules' entry '${moduleName}'`,
341
+ label: `the 'swiftpmConfig.modules' entry '${moduleName}'`,
290
342
  remedy,
291
343
  extraReservedNames: reservedNamesForRun(),
292
344
  });
293
- const clash = taken.get(moduleName.toLowerCase());
345
+ const clash = taken.get(swiftNameKey(moduleName));
294
346
  if (clash != null) {
295
347
  throw new SpmNameCollisionError(
296
- `react-native autolinking: SPM Swift name collision: the 'spm.modules' entry '${moduleName}' ` +
297
- (clash === moduleName
298
- ? `is already the name of another autolinked target.`
299
- : `differs from the existing target '${clash}' only in case, which collides on case-insensitive filesystems.`) +
348
+ `react-native autolinking: SPM Swift name collision: the 'swiftpmConfig.modules' entry '${moduleName}' ` +
349
+ moduleClashDiagnosis(moduleName, clash) +
300
350
  ` ${remedy}`,
301
351
  );
302
352
  }
303
353
  }
304
354
 
305
355
  /**
306
- * Reads the app's `spm.denyPlugins` — npm names of autolinking plugins to
307
- * skip. The escape hatch for the transitive plugin discovery (an app opts a
308
- * framework's plugin OUT); no allowlist is required.
356
+ * The app's `denyPlugins` — npm names of autolinking plugins to skip. The
357
+ * escape hatch for transitive plugin discovery (an app opts a framework's
358
+ * plugin OUT); no allowlist is required.
309
359
  */
310
360
  function readDenyPluginsFromConfig(appRoot /*: string */) /*: Array<string> */ {
311
- const configPath = path.join(appRoot, 'react-native.config.js');
312
- if (!fs.existsSync(configPath)) {
313
- return [];
314
- }
315
- try {
316
- // $FlowFixMe[unsupported-syntax] dynamic require by computed path
317
- const config = require(configPath);
318
- return config.spm?.denyPlugins ?? [];
319
- } catch (e) {
320
- return [];
321
- }
361
+ return stringList(appConfigField(appRoot, 'denyPlugins')?.value);
322
362
  }
323
363
 
324
364
  /**
@@ -433,21 +473,9 @@ function findSelfManagedPackageDir(absSource /*: string */) /*: ?string */ {
433
473
  * the scaffolder translates the podspec into a Package.swift.
434
474
  */
435
475
  function hasPodspec(absSource /*: string */) /*: boolean */ {
436
- for (const sub of ['', 'ios']) {
437
- const dir = sub === '' ? absSource : path.join(absSource, sub);
438
- try {
439
- if (
440
- fs
441
- .readdirSync(dir)
442
- .some(e => e.endsWith('.podspec') && !e.startsWith('.spm-scaffold-'))
443
- ) {
444
- return true;
445
- }
446
- } catch {
447
- // dir does not exist; try the next candidate
448
- }
449
- }
450
- return false;
476
+ return [absSource, path.join(absSource, 'ios')].some(
477
+ dir => findPodspecs(dir).length > 0,
478
+ );
451
479
  }
452
480
 
453
481
  /**
@@ -778,9 +806,9 @@ function expandSpmSourceGlobs(
778
806
  * Returns null if the dependency doesn't have iOS support.
779
807
  *
780
808
  * `swiftNameByNpm` maps each autolinked dep's npm name to its resolved Swift
781
- * name (populated by expandSpmDependencies, honoring the dep's `spm.name`
782
- * config and scope disambiguation). Every name this function emits comes from
783
- * there — see requireSwiftName.
809
+ * name (populated by expandSpmDependencies from the dep's podspec and
810
+ * `spm.name`). Every name this function emits comes from there — see
811
+ * requireSwiftName.
784
812
  */
785
813
  /**
786
814
  * Read the dep's podspec (if any) and extract its declared
@@ -799,24 +827,12 @@ function expandSpmSourceGlobs(
799
827
  function extractPodspecHeaderSearchPaths(
800
828
  sourceDir /*: string */,
801
829
  ) /*: Array<string> */ {
802
- let podspecPath /*: ?string */ = null;
803
- try {
804
- const entries = fs.readdirSync(sourceDir);
805
- // Skip a crashed run's leftover `.spm-scaffold-<pid>-<name>.podspec` copy.
806
- const candidate = entries.find(
807
- e => e.endsWith('.podspec') && !e.startsWith('.spm-scaffold-'),
808
- );
809
- if (candidate != null) {
810
- podspecPath = path.join(sourceDir, candidate);
811
- }
812
- } catch {
813
- return [];
814
- }
830
+ const podspecPath /*: ?string */ = findPodspecs(sourceDir)[0];
815
831
  if (podspecPath == null) return [];
816
832
 
817
833
  let model;
818
834
  try {
819
- model = readPodspec(podspecPath);
835
+ model = readPodspecCached(podspecPath);
820
836
  } catch {
821
837
  return [];
822
838
  }
@@ -936,6 +952,8 @@ function generateAutolinkedPackageSwift(
936
952
  input.pluginPackageDeps ?? [];
937
953
  const pluginProductDeps /*: ReadonlyArray<PluginProductDep> */ =
938
954
  input.pluginProductDeps ?? [];
955
+ const iosDeploymentTarget /*: string */ =
956
+ input.iosDeploymentTarget ?? MIN_IOS_VERSION_SUPPORTED;
939
957
 
940
958
  // Package-level dependencies: one .package(path:) per autolinked dep,
941
959
  // plus ReactNative if any inline target needs to import React headers.
@@ -1069,7 +1087,7 @@ import Foundation
1069
1087
 
1070
1088
  ${guardBlock}let package = Package(
1071
1089
  name: "${AUTOLINKED_PACKAGE_NAME}",
1072
- platforms: [.iOS(.v15)],
1090
+ platforms: [.iOS("${iosDeploymentTarget}")],
1073
1091
  products: [
1074
1092
  .library(name: "${AUTOLINKED_PACKAGE_NAME}", targets: ["AutolinkedAggregate"]),
1075
1093
  ],
@@ -1119,6 +1137,8 @@ function generateSynthPackageSwift(spec /*: SynthPackageSpec */) /*: string */ {
1119
1137
  const targetPath /*: string */ = spec.targetPath ?? `Sources/${swiftName}`;
1120
1138
  const siblingSynthAbsolutePaths /*: {[string]: string} */ =
1121
1139
  spec.siblingSynthAbsolutePaths ?? {};
1140
+ const iosDeploymentTarget /*: string */ =
1141
+ spec.iosDeploymentTarget ?? MIN_IOS_VERSION_SUPPORTED;
1122
1142
 
1123
1143
  // Package dependencies — ReactNative + each spm sibling synth package.
1124
1144
  // The React + codegen package paths are plain relative strings computed by
@@ -1217,7 +1237,7 @@ import PackageDescription
1217
1237
 
1218
1238
  let package = Package(
1219
1239
  name: "${swiftName}",
1220
- platforms: [.iOS(.v15)],
1240
+ platforms: [.iOS("${iosDeploymentTarget}")],
1221
1241
  products: [
1222
1242
  .library(name: "${swiftName}"${isDynamic ? ', type: .dynamic' : ''}, targets: ["${swiftName}"]),
1223
1243
  ],
@@ -1312,8 +1332,8 @@ function main(argv /*:: ?: Array<string> */) /*: void */ {
1312
1332
  const allDeps = expandSpmDependencies(directDeps, {
1313
1333
  readConfig: defaultReadConfig,
1314
1334
  resolveDep: defaultResolveDep,
1335
+ readPodspec: defaultReadPodspec,
1315
1336
  extraReservedNames: reservedNamesForRun(),
1316
- log,
1317
1337
  });
1318
1338
 
1319
1339
  // Map every autolinked npm name to its resolved Swift name so transitive
@@ -1380,7 +1400,7 @@ function main(argv /*:: ?: Array<string> */) /*: void */ {
1380
1400
  const dependents = pluginHostDependents.get(dep.name);
1381
1401
  if (dependents != null) {
1382
1402
  throw new Error(
1383
- `react-native autolinking: '${dep.name}' ships an SPM autolinking plugin, which owns its native contribution — so React Native does not build it as a sibling target for anything to depend on. It is declared in 'spm.dependencies' by ${dependents.map(name => `'${name}'`).join(', ')}. Remove it there; nothing is lost. Its plugin links its products into the app and resolves its own ecosystem's dependencies, so a library that builds against it does not declare it here.`,
1403
+ `react-native autolinking: '${dep.name}' ships an SPM autolinking plugin, which owns its native contribution — so React Native does not build it as a sibling target for anything to depend on. It is declared as a SwiftPM dependency by ${dependents.map(name => `'${name}'`).join(', ')}. Remove it there; nothing is lost. Its plugin links its products into the app and resolves its own ecosystem's dependencies, so a library that builds against it does not declare it here.`,
1384
1404
  );
1385
1405
  }
1386
1406
  log(
@@ -1414,17 +1434,20 @@ function main(argv /*:: ?: Array<string> */) /*: void */ {
1414
1434
  // If the module declares `sources: [glob, ...]` (CocoaPods-style), expand
1415
1435
  // the globs now relative to its dir and attach the file list to the target
1416
1436
  // so the emission loop below renders `sources: [...]` literally.
1417
- const configModules = readSpmModulesFromConfig(appRoot);
1437
+ const {modules: configModules, root: configModulesRoot} =
1438
+ readSpmModulesFromConfig(appRoot);
1418
1439
  // Module names land in the manifest exactly as written, so they get the same
1419
1440
  // checks a dep's Swift name gets. Seeded with the dep target names already
1420
1441
  // emitted so a module can't shadow an autolinked library either.
1421
1442
  const takenSwiftNames /*: Map<string, string> */ = new Map(
1422
- entries.map(entry => [entry.target.name.toLowerCase(), entry.target.name]),
1443
+ entries.map(entry => [swiftNameKey(entry.target.name), entry.target.name]),
1423
1444
  );
1424
1445
  for (const mod of configModules) {
1425
1446
  assertSpmModuleName(mod.name, takenSwiftNames);
1426
- takenSwiftNames.set(mod.name.toLowerCase(), mod.name);
1427
- const absPath = path.resolve(appRoot, mod.path);
1447
+ takenSwiftNames.set(swiftNameKey(mod.name), mod.name);
1448
+ // Relative to the package.json (or config file) that declared it, so a
1449
+ // path reads correctly from where it is written.
1450
+ const absPath = path.resolve(configModulesRoot, mod.path);
1428
1451
  const relPath = path.relative(outputDir, absPath);
1429
1452
  const userSources =
1430
1453
  Array.isArray(mod.sources) && mod.sources.length > 0
@@ -1689,6 +1712,7 @@ function main(argv /*:: ?: Array<string> */) /*: void */ {
1689
1712
 
1690
1713
  const synthContent = generateSynthPackageSwift({
1691
1714
  swiftName: target.name,
1715
+ iosDeploymentTarget: args.iosDeploymentTarget,
1692
1716
  exclude: prefixedExclude,
1693
1717
  sources: prefixedSources,
1694
1718
  // Stub include/ subdir lives in the wrapper dir; satisfies SPM's
@@ -1863,6 +1887,7 @@ function main(argv /*:: ?: Array<string> */) /*: void */ {
1863
1887
  // autolinked dep is a real SPM package in its own source dir.
1864
1888
  const aggregatorContent = generateAutolinkedPackageSwift({
1865
1889
  npmDeps: aggregatorPackageDeps,
1890
+ iosDeploymentTarget: args.iosDeploymentTarget,
1866
1891
  hasReactDep,
1867
1892
  xcframeworksRelPath,
1868
1893
  pluginPackageDeps,
@@ -1999,6 +2024,7 @@ module.exports = {
1999
2024
  findSelfManagedPackageDir,
2000
2025
  hasPodspec,
2001
2026
  hasMixedLanguageSources,
2027
+ readDenyPluginsFromConfig,
2002
2028
  MissingManifestError,
2003
2029
  reportMissingManifests,
2004
2030
  AUTOGEN_MARKER,
@@ -319,7 +319,11 @@ function shellScriptPhase(
319
319
  outputFileListPaths: empty,
320
320
  outputPaths: pathList(options.outputPaths),
321
321
  runOnlyForDeploymentPostprocessing: '0',
322
- shellPath: '/bin/sh',
322
+ // React Native's own bodies here start with `set -euo pipefail`, which
323
+ // /bin/sh rejects on a host where it isn't bash (e.g. dash's `set -o
324
+ // pipefail: Illegal option`). Run every phase under bash, including
325
+ // plugin-contributed ones, so a plugin's body can rely on it too.
326
+ shellPath: '/bin/bash',
323
327
  shellScript: quoteIfNeeded(script),
324
328
  },
325
329
  };
@@ -899,7 +903,8 @@ function generateXcscheme(
899
903
  ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
900
904
  <ActionContent
901
905
  title = "Sync SPM Autolinking"
902
- scriptText = "${escapedSync}">
906
+ scriptText = "${escapedSync}"
907
+ shellToInvoke = "/bin/bash">
903
908
  <EnvironmentBuildable>
904
909
  <BuildableReference
905
910
  BuildableIdentifier = "primary"
@@ -1361,27 +1366,29 @@ function injectSpmIntoPbxproj(
1361
1366
  // bundles JS via its own phase.
1362
1367
  const syncScript = buildSyncAutolinkingScript(reactNativePath);
1363
1368
  const syncPhaseUuid = mkUuid('PBXShellScriptBuildPhase', 'SyncAutolinking');
1369
+ const syncEntry = shellScriptPhase(
1370
+ syncPhaseUuid,
1371
+ 'Sync SPM Autolinking',
1372
+ syncScript,
1373
+ );
1364
1374
  if (!text.includes(syncPhaseUuid)) {
1365
1375
  text = insertObjectsIntoSection(
1366
1376
  text,
1367
1377
  'PBXShellScriptBuildPhase',
1368
- serializeEntry(
1369
- shellScriptPhase(syncPhaseUuid, 'Sync SPM Autolinking', syncScript),
1370
- ),
1378
+ serializeEntry(syncEntry),
1371
1379
  );
1372
1380
  } else {
1373
- // Already injected on a prior run — the phase object owns its
1374
- // shellScript, so refresh it in place (same quoting used at creation) in
1375
- // case the generated script changed since. Byte-identical when it
1376
- // didn't; field order and every other byte of the phase are untouched.
1377
- const existingPhase = findObjectByUuid(text, syncPhaseUuid);
1378
- if (existingPhase != null) {
1379
- text = setScalarField(
1380
- text,
1381
- existingPhase,
1382
- 'shellScript',
1383
- quoteIfNeeded(syncScript),
1384
- );
1381
+ // Already injected on a prior run — the phase object owns these fields,
1382
+ // so refresh them in place (same quoting used at creation) in case the
1383
+ // generated script changed, or a project injected before shellPath moved
1384
+ // to bash (see shellScriptPhase) still has the stale value. Byte-identical
1385
+ // when neither did; field order and every other byte of the phase are
1386
+ // untouched.
1387
+ for (const key of ['shellScript', 'shellPath']) {
1388
+ const current = findObjectByUuid(text, syncPhaseUuid);
1389
+ if (current != null) {
1390
+ text = setScalarField(text, current, key, syncEntry.fields[key]);
1391
+ }
1385
1392
  }
1386
1393
  }
1387
1394
  injectedUuids.push(syncPhaseUuid);
@@ -1429,7 +1436,12 @@ function injectSpmIntoPbxproj(
1429
1436
  } else {
1430
1437
  const existingPhase = findObjectByUuid(text, embedPhaseUuid);
1431
1438
  if (existingPhase != null) {
1432
- for (const key of ['shellScript', 'inputPaths', 'outputPaths']) {
1439
+ for (const key of [
1440
+ 'shellScript',
1441
+ 'inputPaths',
1442
+ 'outputPaths',
1443
+ 'shellPath',
1444
+ ]) {
1433
1445
  const current = findObjectByUuid(text, embedPhaseUuid);
1434
1446
  if (current != null) {
1435
1447
  text = setScalarField(text, current, key, embedEntry.fields[key]);
@@ -1606,6 +1618,7 @@ function injectSpmIntoPbxproj(
1606
1618
  'shellScript',
1607
1619
  'inputPaths',
1608
1620
  'outputPaths',
1621
+ 'shellPath',
1609
1622
  ]) {
1610
1623
  const current = findObjectByUuid(text, uuid);
1611
1624
  if (current != null) {
@@ -1876,11 +1889,40 @@ function addPreActionToScheme(
1876
1889
  // value itself never contains one — the next `"` is always the closing
1877
1890
  // delimiter.
1878
1891
  const valueEnd = xml.indexOf('"', valueStart);
1879
- return (
1892
+ let xmlWithScript =
1880
1893
  xml.slice(0, valueStart) +
1881
1894
  escapeXmlAttribute(syncScript) +
1882
- xml.slice(valueEnd)
1895
+ xml.slice(valueEnd);
1896
+
1897
+ // Xcode always runs a scheme pre-action's scriptText under the shell this
1898
+ // attribute names (default /bin/sh), independent of a
1899
+ // PBXShellScriptBuildPhase's own shellPath (see shellScriptPhase) — pin
1900
+ // it to bash too. `>` can't appear unescaped inside either attribute
1901
+ // value, so it reliably closes the ActionContent open tag. Search the
1902
+ // whole open tag (not just after scriptText) and allow any spacing
1903
+ // around `=`, since attribute order and formatting aren't guaranteed.
1904
+ const contentOpenIdx = xmlWithScript.lastIndexOf(
1905
+ '<ActionContent',
1906
+ titleIdx,
1883
1907
  );
1908
+ const contentCloseIdx = xmlWithScript.indexOf('>', stIdx);
1909
+ const openTag = xmlWithScript.slice(contentOpenIdx, contentCloseIdx);
1910
+ const stiMatch = openTag.match(/shellToInvoke\s*=\s*"/);
1911
+ if (stiMatch != null) {
1912
+ const stiValueStart =
1913
+ contentOpenIdx + stiMatch.index + stiMatch[0].length;
1914
+ const stiValueEnd = xmlWithScript.indexOf('"', stiValueStart);
1915
+ xmlWithScript =
1916
+ xmlWithScript.slice(0, stiValueStart) +
1917
+ '/bin/bash' +
1918
+ xmlWithScript.slice(stiValueEnd);
1919
+ } else {
1920
+ xmlWithScript =
1921
+ xmlWithScript.slice(0, contentCloseIdx) +
1922
+ '\n shellToInvoke = "/bin/bash"' +
1923
+ xmlWithScript.slice(contentCloseIdx);
1924
+ }
1925
+ return xmlWithScript;
1884
1926
  }
1885
1927
  const refMatch = xml.match(
1886
1928
  new RegExp(
@@ -1907,7 +1949,8 @@ function addPreActionToScheme(
1907
1949
  ` ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">\n` +
1908
1950
  ` <ActionContent\n` +
1909
1951
  ` title = "Sync SPM Autolinking"\n` +
1910
- ` scriptText = "${escapeXmlAttribute(syncScript)}">\n` +
1952
+ ` scriptText = "${escapeXmlAttribute(syncScript)}"\n` +
1953
+ ` shellToInvoke = "/bin/bash">\n` +
1911
1954
  ` <EnvironmentBuildable>\n` +
1912
1955
  ` ${cleanRef}\n` +
1913
1956
  ` </EnvironmentBuildable>\n` +
@@ -2181,7 +2224,7 @@ function readScriptPhasesManifest(
2181
2224
  */
2182
2225
  function readMarker(
2183
2226
  xcodeprojPath /*: string */,
2184
- ) /*: ?{generatedSources?: {[string]: Array<string>}, scriptPhases?: {[string]: string}, artifactsVersionOverride?: ?string, configCommand?: ?Array<string>, buildSettingChanges?: Array<BuildSettingChange>, createdArrayFields?: Array<CreatedArrayField>, scheme?: {file?: ?string, created?: ?boolean}, ...} */ {
2227
+ ) /*: ?{targetUuid?: ?string, generatedSources?: {[string]: Array<string>}, scriptPhases?: {[string]: string}, artifactsVersionOverride?: ?string, configCommand?: ?Array<string>, buildSettingChanges?: Array<BuildSettingChange>, createdArrayFields?: Array<CreatedArrayField>, scheme?: {file?: ?string, created?: ?boolean}, ...} */ {
2185
2228
  const markerPath = path.join(xcodeprojPath, SPM_INJECTED_MARKER);
2186
2229
  try {
2187
2230
  // $FlowFixMe[incompatible-return] JSON.parse returns any
@@ -2718,6 +2761,7 @@ module.exports = {
2718
2761
  buildSchemePreActionScript,
2719
2762
  buildEmbedFrameworksScript,
2720
2763
  flavorForBuildConfiguration,
2764
+ targetBuildConfigUuids,
2721
2765
  frameworkConditionalSettings,
2722
2766
  ensureStubPackages,
2723
2767
  buildSpmDependencyGraph,
@@ -2732,6 +2776,7 @@ module.exports = {
2732
2776
  addPreActionToScheme,
2733
2777
  removePreActionFromScheme,
2734
2778
  findInjectedXcodeproj,
2779
+ readMarker,
2735
2780
  readArtifactsVersionOverride,
2736
2781
  readPinnedConfigCommand,
2737
2782
  readScriptPhasesManifest,