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

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 (52) 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/FBReactNativeSpecJSI.h +8 -0
  7. package/ReactAndroid/gradle.properties +1 -1
  8. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +7 -1
  9. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +11 -1
  10. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +3 -1
  11. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +3 -1
  12. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +12 -1
  13. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Canary_Android.kt +3 -1
  14. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +3 -1
  15. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  16. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +15 -1
  17. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +4 -1
  18. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  19. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +5 -1
  20. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +6 -1
  21. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +74 -56
  22. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +4 -2
  23. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +5 -1
  24. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +10 -1
  25. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSCanary.h +5 -1
  26. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +2 -1
  27. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +6 -1
  28. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +3 -1
  29. package/package.json +10 -10
  30. package/scripts/codegen/generate-artifacts-executor/generateRCTThirdPartyComponents.js +18 -4
  31. package/scripts/setup-apple-spm.js +82 -4
  32. package/scripts/spm/autolinking-plugins.js +8 -5
  33. package/scripts/spm/expand-spm-dependencies.js +217 -156
  34. package/scripts/spm/generate-spm-autolinking.js +114 -88
  35. package/scripts/spm/generate-spm-xcodeproj.js +67 -22
  36. package/scripts/spm/ios-deployment-target.js +378 -0
  37. package/scripts/spm/read-podspec.js +156 -8
  38. package/scripts/spm/scaffold-package-swift.js +272 -105
  39. package/scripts/spm/spm-pbxproj.js +36 -21
  40. package/scripts/spm/spm-types.js +25 -8
  41. package/scripts/spm/spm-utils.js +26 -0
  42. package/scripts/spm/swiftpm-config.js +245 -0
  43. package/scripts/spm/sync-spm-autolinking.js +11 -2
  44. package/sdks/hermes-engine/version.properties +1 -1
  45. package/src/private/featureflags/ReactNativeFeatureFlags.js +10 -5
  46. package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +2 -1
  47. package/src/private/webapis/dom/nodes/ReactNativeElement.js +6 -4
  48. package/src/private/webapis/dom/nodes/ReadOnlyText.js +6 -4
  49. package/src/react-private-interface.js +4 -0
  50. package/src/react-private-interface.js.flow +2 -0
  51. package/types_generated/Libraries/ReactNative/FabricUIManager.d.ts +47 -0
  52. package/types_generated/src/react-private-interface.d.ts +4 -1
@@ -10,53 +10,86 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- const {RESERVED_SWIFT_NAMES, makeLogger, toSwiftName} = require('./spm-utils');
13
+ const {
14
+ findPodspecs,
15
+ readPodspecCached,
16
+ readPodspecNames,
17
+ } = require('./read-podspec');
18
+ const {
19
+ RESERVED_SWIFT_NAMES,
20
+ isValidSwiftName,
21
+ makeLogger,
22
+ swiftNameKey,
23
+ toC99Name,
24
+ toSwiftName,
25
+ } = require('./spm-utils');
26
+ const {readSwiftpmConfig, stringList} = require('./swiftpm-config');
14
27
  const fs = require('node:fs');
15
28
  const path = require('node:path');
16
29
 
17
30
  const {warn} = makeLogger('expand-spm-dependencies');
18
31
 
19
32
  /**
20
- * expand-spm-dependencies.js — Resolves transitive native deps declared via
21
- * `spm.dependencies` in a library's react-native.config.js.
33
+ * expand-spm-dependencies.js — Resolves transitive native deps a library
34
+ * declares in its `swiftpmConfig`.
22
35
  *
23
36
  * SPM has no equivalent of CocoaPods' podspec `s.dependency`, so library
24
37
  * authors declare the same relationships explicitly:
25
38
  *
26
- * // react-native-reanimated/react-native.config.js
27
- * module.exports = {
28
- * dependency: { platforms: { ios: {} } },
29
- * spm: { dependencies: ['react-native-worklets'] },
30
- * };
39
+ * // react-native-reanimated/package.json
40
+ * { "swiftpmConfig": { "dependencies": ["react-native-worklets"] } }
31
41
  *
32
42
  * This module reads the directly-autolinked deps (from autolinking.json),
33
- * follows each one's spm.dependencies recursively, and returns the deduped
43
+ * follows each one's declared dependencies recursively, and returns the deduped
34
44
  * list with autolinking-shaped entries so the downstream pipeline can convert
35
45
  * each to an SPM target without further branching.
36
46
  *
37
- * I/O is injected (readConfig, resolveDep, log) so the logic stays pure and
38
- * testable.
47
+ * It also resolves each dep's Swift target name — see resolveSwiftName.
48
+ *
49
+ * I/O is injected (readConfig, resolveDep, readPodspec) so the logic stays pure
50
+ * and testable.
39
51
  */
40
52
 
41
53
  /*::
42
54
  import type {AutolinkedDep} from './spm-types';
55
+ import type {SwiftpmConfig} from './swiftpm-config';
43
56
 
44
57
  // react-native.config.js entries have a user-defined shape, so we use an
45
58
  // inexact object type and access properties dynamically.
46
59
  type RnConfig = {...};
47
60
  type ReadConfig = (root: string) => ?RnConfig;
48
61
  type ResolveDep = (name: string, fromRoot: string) => ?string;
49
- type Log = (message: string) => void;
50
- // Keyed by lower case, valued with the canonical spelling: two names differing
51
- // only in case are not distinct enough for the build to keep the two apart.
62
+ // Where a resolved name came from: what the library declared, what its podspec
63
+ // says, or the npm-name guess. `podspecKey` narrows a podspec-sourced name to
64
+ // the field it was read from — `header_dir` and `module_name` can name the same
65
+ // library differently, and only one of them wins.
66
+ type PodspecNameKey = 'header_dir' | 'module_name' | 'name';
67
+ type ResolvedSwiftName = {
68
+ name: string,
69
+ source: 'config' | 'podspec' | 'npm',
70
+ podspecKey?: PodspecNameKey,
71
+ };
72
+ // The podspec fields that name a library. A PodspecModel satisfies it.
73
+ type PodspecFacts = {
74
+ readonly name?: ?string,
75
+ readonly moduleName?: ?string,
76
+ readonly headerDir?: ?string,
77
+ ...
78
+ };
79
+ type ReadPodspec = (root: string, podspecPath: ?string) => ?PodspecFacts;
80
+ type ReadSwiftpmConfig = (root: string, rnConfig: ?RnConfig) => ?SwiftpmConfig;
81
+ // Keyed by swiftNameKey, valued with the canonical spelling: a name that only
82
+ // differs from a reserved one in case or punctuation is not distinct enough for
83
+ // the build to keep the two apart.
52
84
  type ReservedNames = ReadonlyMap<string, string>;
53
85
  type Options = {
54
86
  readConfig: ReadConfig,
55
87
  resolveDep: ResolveDep,
88
+ readPodspec?: ?ReadPodspec,
89
+ readSwiftpmConfig?: ?ReadSwiftpmConfig,
56
90
  // Names to reserve alongside RESERVED_SWIFT_NAMES, supplied by the caller
57
91
  // (remote mode relabels the RN package) since this module reads no config.
58
92
  extraReservedNames?: ?ReadonlyArray<string>,
59
- log?: ?Log,
60
93
  };
61
94
  */
62
95
 
@@ -71,74 +104,133 @@ class SpmNameCollisionError extends Error {
71
104
  }
72
105
  }
73
106
 
74
- // The charset `spm.name` must satisfy — permissive on purpose, since it has to
75
- // admit header-dir style (lowercase with hyphens) as well as Swift identifiers.
76
- // Shared with the app's own `spm.modules` names.
77
- function isValidSwiftName(name /*: unknown */) /*: boolean */ {
78
- return typeof name === 'string' && /^[A-Za-z_][A-Za-z0-9_-]*$/.test(name);
79
- }
80
-
81
107
  function reservedSwiftNames(
82
108
  extraReservedNames /*: ?ReadonlyArray<string> */,
83
109
  ) /*: ReservedNames */ {
84
110
  return new Map(
85
111
  [...RESERVED_SWIFT_NAMES, ...(extraReservedNames ?? [])].map(name => [
86
- name.toLowerCase(),
112
+ swiftNameKey(name),
87
113
  name,
88
114
  ]),
89
115
  );
90
116
  }
91
117
 
92
- // The scope-borrowed form of a name: `@powersync/react-native`'s `ReactNative`
93
- // becomes `PowersyncReactNative`.
94
- function scopeBorrowedName(
118
+ // The prefix a podspec publishes its headers under, and the field it came from
119
+ // — or null when the podspec declares none. A prefix Swift cannot spell is
120
+ // normalized rather than abandoned: reverting to the npm name would silently
121
+ // produce a prefix nothing imports.
122
+ function podspecSwiftName(
95
123
  npmName /*: string */,
96
- swiftName /*: string */,
97
- ) /*: ?string */ {
98
- const scope = /^@([^/]+)\//.exec(npmName)?.[1];
99
- return scope == null ? null : `${toSwiftName(scope)}${swiftName}`;
124
+ podspec /*: ?PodspecFacts */,
125
+ ) /*: ?{name: string, key: PodspecNameKey} */ {
126
+ // CocoaPods' own order is `module_name` first (specification.rb), but our
127
+ // single name is the ObjC include prefix as well as the Swift module: a
128
+ // declared `header_dir` IS the prefix a library's consumers write, so it keeps
129
+ // winning. `module_name` comes next, being what Swift and `@import` consumers
130
+ // spell, and the pod name — often dashed — is the last resort.
131
+ const candidates /*: ReadonlyArray<[PodspecNameKey, ?string]> */ = [
132
+ ['header_dir', podspec?.headerDir],
133
+ ['module_name', podspec?.moduleName],
134
+ ['name', podspec?.name],
135
+ ];
136
+ for (const [key, candidate] of candidates) {
137
+ if (typeof candidate !== 'string' || candidate.length === 0) {
138
+ continue;
139
+ }
140
+ // Ruby the evaluator never ran: what the prefix would be is unknowable, so
141
+ // the npm name is the honest answer — and, not being derived, it is not
142
+ // recorded in the library's package.json.
143
+ if (candidate.includes('#{')) {
144
+ return null;
145
+ }
146
+ if (isValidSwiftName(candidate)) {
147
+ return {name: candidate, key};
148
+ }
149
+ const normalized = toC99Name(candidate);
150
+ warn(
151
+ `'${npmName}' declares the podspec prefix '${candidate}', which is not a valid Swift target name; using '${normalized}'. ` +
152
+ `Set 'swiftpmConfig.name' in ${npmName}'s package.json to choose the prefix yourself.`,
153
+ );
154
+ return {name: normalized, key};
155
+ }
156
+ return null;
100
157
  }
101
158
 
102
- // The Swift target name for one dep, judged in isolation. `spm.name` is for
103
- // libraries whose import prefix differs from the derived name:
104
- // `react-native-worklets` ships headers as `<worklets/...>` (podspec
105
- // `s.header_dir`), so its target is `worklets`, not `ReactNativeWorklets`. A
106
- // derived name that lands on a reserved one borrows the npm scope instead.
159
+ /**
160
+ * The Swift target name for one dep, which is also the prefix its headers are
161
+ * imported under (`#import <Name/Header.h>`), and where it came from. The name
162
+ * a library declares wins; the podspec is the transitional source of truth
163
+ * behind it — `react-native-svg` publishes `RNSVG`, not `ReactNativeSvg` — in
164
+ * the order `header_dir` → `module_name` → pod name. The npm name is the last
165
+ * resort, for a library that ships a `Package.swift` and no podspec.
166
+ *
167
+ * The source is what tells the scaffolder which names are safe to write into a
168
+ * library's package.json: a derived one, never a guessed one.
169
+ */
107
170
  function resolveSwiftName(
108
171
  npmName /*: string */,
109
- config /*: ?RnConfig */,
110
- reserved /*: ReservedNames */,
111
- log /*:: ?: ?Log */,
112
- ) /*: string */ {
113
- // $FlowFixMe[prop-missing] config has dynamic shape
114
- const override = config?.spm?.name;
115
- if (override != null) {
116
- if (typeof override !== 'string' || override.length === 0) {
172
+ spmConfig /*: ?SwiftpmConfig */,
173
+ podspec /*: ?PodspecFacts */,
174
+ ) /*: ResolvedSwiftName */ {
175
+ const declared = spmConfig?.name;
176
+ if (declared != null) {
177
+ if (typeof declared !== 'string' || declared.length === 0) {
117
178
  throw new Error(
118
- `react-native autolinking: '${npmName}' has an invalid 'spm.name' override: expected a non-empty string, got ${JSON.stringify(override)}.`,
179
+ `react-native autolinking: '${npmName}' declares an invalid SwiftPM name: expected a non-empty string, got ${String(declared)}. Set 'swiftpmConfig.name' in its package.json.`,
119
180
  );
120
181
  }
121
- if (!isValidSwiftName(override)) {
182
+ if (!isValidSwiftName(declared)) {
122
183
  throw new Error(
123
- `react-native autolinking: '${npmName}' has an invalid 'spm.name' override '${override}': must start with a letter or underscore and contain only letters, digits, underscores, or hyphens.`,
184
+ `react-native autolinking: '${npmName}' declares an invalid SwiftPM name '${declared}': must start with a letter or underscore and contain only letters, digits, underscores, or hyphens. Set 'swiftpmConfig.name' in its package.json.`,
124
185
  );
125
186
  }
126
- return override;
187
+ return {name: declared, source: 'config'};
127
188
  }
128
189
 
129
- const derived = toSwiftName(npmName);
130
- if (!reserved.has(derived.toLowerCase())) {
131
- return derived;
190
+ const fromPodspec = podspecSwiftName(npmName, podspec);
191
+ if (fromPodspec != null) {
192
+ return {
193
+ name: fromPodspec.name,
194
+ source: 'podspec',
195
+ podspecKey: fromPodspec.key,
196
+ };
132
197
  }
133
- const disambiguated = scopeBorrowedName(npmName, derived);
134
- if (disambiguated == null || reserved.has(disambiguated.toLowerCase())) {
135
- return derived;
198
+ const fromNpm = toSwiftName(npmName);
199
+ if (podspec != null) {
200
+ warn(
201
+ `'${npmName}' ships a podspec React Native could not read a name from (CocoaPods may not be installed), so it is named '${fromNpm}' after its npm package instead. ` +
202
+ `A machine that can read the podspec may resolve a different name — set 'swiftpmConfig.name' in ${npmName}'s package.json to settle it everywhere.`,
203
+ );
136
204
  }
137
- log?.(
138
- `'${npmName}' would take React Native's reserved name '${derived}', so its npm scope is prepended: '${disambiguated}'. ` +
139
- `Set 'spm.name' in ${npmName}'s react-native.config.js to choose the name yourself.`,
140
- );
141
- return disambiguated;
205
+ return {name: fromNpm, source: 'npm'};
206
+ }
207
+
208
+ function collisionDiagnosis(
209
+ existing /*: string */,
210
+ swiftName /*: string */,
211
+ ) /*: string */ {
212
+ if (existing === swiftName) {
213
+ return `both resolve to '${swiftName}'.`;
214
+ }
215
+ if (existing.toLowerCase() === swiftName.toLowerCase()) {
216
+ return `differ only in case, which collides on case-insensitive filesystems.`;
217
+ }
218
+ return `both compile as the module '${toC99Name(swiftName)}' — SwiftPM replaces every character C99 rejects.`;
219
+ }
220
+
221
+ // Vaguer about the clash than the dep-vs-dep message on purpose: this set spans
222
+ // package identities and product names, which collide differently.
223
+ function reservedDiagnosis(
224
+ swiftName /*: string */,
225
+ reservedName /*: string */,
226
+ ) /*: string */ {
227
+ if (reservedName === swiftName) {
228
+ return `which React Native reserves for its own SPM package and products.`;
229
+ }
230
+ if (reservedName.toLowerCase() === swiftName.toLowerCase()) {
231
+ return `which differs from React Native's reserved '${reservedName}' only in case — not distinct enough for the build to keep the two apart.`;
232
+ }
233
+ return `which compiles as the same module as React Native's reserved '${reservedName}'.`;
142
234
  }
143
235
 
144
236
  function assertNameNotReserved(
@@ -146,24 +238,21 @@ function assertNameNotReserved(
146
238
  reserved /*: ReservedNames */,
147
239
  labels /*: {label: string, remedy: string} */,
148
240
  ) /*: void */ {
149
- const reservedName = reserved.get(swiftName.toLowerCase());
241
+ const reservedName = reserved.get(swiftNameKey(swiftName));
150
242
  if (reservedName == null) {
151
243
  return;
152
244
  }
153
- // Vaguer about the case clash than the dep-vs-dep message on purpose: this
154
- // set spans package identities and product names, which collide differently.
155
245
  throw new SpmNameCollisionError(
156
246
  `react-native autolinking: SPM Swift name collision: ${labels.label} resolves to '${swiftName}', ` +
157
- (reservedName === swiftName
158
- ? `which React Native reserves for its own SPM package and products.`
159
- : `which differs from React Native's reserved '${reservedName}' only in case — not distinct enough for the build to keep the two apart.`) +
247
+ reservedDiagnosis(swiftName, reservedName) +
160
248
  ` ${labels.remedy}`,
161
249
  );
162
250
  }
163
251
 
164
252
  /**
165
253
  * Throws when `swiftName` is one React Native's own manifests use. `remedy` is
166
- * the fix: a library sets `spm.name`, an app renames its `spm.modules` entry.
254
+ * the fix: a library sets `swiftpmConfig.name`, an app renames its
255
+ * `swiftpmConfig.modules` entry.
167
256
  */
168
257
  function assertSwiftNameNotReserved(
169
258
  swiftName /*: string */,
@@ -194,83 +283,36 @@ function assertNoReservedSwiftNames(
194
283
  }
195
284
  assertNameNotReserved(swiftName, reserved, {
196
285
  label: `'${dep.name}'`,
197
- remedy: `Set a different 'spm.name' in ${dep.name}'s react-native.config.js.`,
286
+ remedy: `Set a different 'swiftpmConfig.name' in ${dep.name}'s package.json.`,
198
287
  });
199
288
  }
200
289
  }
201
290
 
202
- // Pulls apart deps that resolved to the same name by borrowing their npm scopes.
203
- // Every scoped member of a colliding group moves: there is no non-arbitrary
204
- // winner to keep. Exactly one pass — retrying would trade a diagnosable error
205
- // for a name nobody can predict.
206
- function disambiguateSharedSwiftNames(
207
- deps /*: ReadonlyArray<AutolinkedDep> */,
208
- autoNamed /*: ReadonlySet<string> */,
209
- log /*: ?Log */,
210
- ) /*: void */ {
211
- const groups /*: Map<string, Array<{dep: AutolinkedDep, swiftName: string}>> */ =
212
- new Map();
213
- for (const dep of deps) {
214
- const swiftName = dep.swiftName;
215
- if (swiftName == null) {
216
- continue;
217
- }
218
- const key = swiftName.toLowerCase();
219
- const group = groups.get(key);
220
- if (group == null) {
221
- groups.set(key, [{dep, swiftName}]);
222
- } else {
223
- group.push({dep, swiftName});
224
- }
225
- }
226
-
227
- for (const group of groups.values()) {
228
- if (group.length < 2) {
229
- continue;
230
- }
231
- for (const {dep, swiftName} of group) {
232
- // A name we derived can borrow a second time (`AAReactNative`); the
233
- // member whose name we did not derive is the incumbent and keeps it.
234
- if (!autoNamed.has(dep.name)) {
235
- continue;
236
- }
237
- const borrowed = scopeBorrowedName(dep.name, swiftName);
238
- if (borrowed == null) {
239
- continue;
240
- }
241
- const others = group
242
- .filter(other => other.dep !== dep)
243
- .map(other => `'${other.dep.name}'`)
244
- .join(', ');
245
- log?.(
246
- `'${dep.name}' would share the name '${swiftName}' with ${others}, so its npm scope is prepended: '${borrowed}'. ` +
247
- `Set 'spm.name' in ${dep.name}'s react-native.config.js to choose the name yourself.`,
248
- );
249
- dep.swiftName = borrowed;
250
- }
251
- }
252
- }
253
-
254
291
  function expandSpmDependencies(
255
292
  directDeps /*: Array<AutolinkedDep> */,
256
293
  options /*: Options */,
257
294
  ) /*: Array<AutolinkedDep> */ {
258
- const {readConfig, resolveDep, extraReservedNames, log} = options;
295
+ const {readConfig, resolveDep, readPodspec, extraReservedNames} = options;
296
+ const spmConfigOf = options.readSwiftpmConfig ?? readSwiftpmConfig;
259
297
  const reserved = reservedSwiftNames(extraReservedNames);
260
298
  const byName /*: Map<string, AutolinkedDep> */ = new Map();
261
299
  for (const dep of directDeps) {
262
300
  byName.set(dep.name, {...dep, spmDependencies: []});
263
301
  }
264
- const autoNamed /*: Set<string> */ = new Set();
265
- const resolveName = (
266
- npmName /*: string */,
267
- config /*: ?RnConfig */,
268
- ) /*: string */ => {
269
- // $FlowFixMe[prop-missing] config has dynamic shape
270
- if (config?.spm?.name == null) {
271
- autoNamed.add(npmName);
302
+ // A podspec that cannot be read leaves the name to the next precedence step,
303
+ // rather than failing a build over a file only CocoaPods needs.
304
+ const podspecFor = (
305
+ root /*: string */,
306
+ podspecPath /*: ?string */,
307
+ ) /*: ?PodspecFacts */ => {
308
+ if (readPodspec == null) {
309
+ return null;
310
+ }
311
+ try {
312
+ return readPodspec(root, podspecPath);
313
+ } catch {
314
+ return null;
272
315
  }
273
- return resolveSwiftName(npmName, config, reserved, log);
274
316
  };
275
317
 
276
318
  const queue /*: Array<string> */ = directDeps.map(d => d.name);
@@ -283,14 +325,18 @@ function expandSpmDependencies(
283
325
  if (current == null) {
284
326
  continue;
285
327
  }
286
- const config = readConfig(current.root);
287
- // Resolve swiftName lazily from the same config read we already need for
288
- // spm.dependencies — saves a duplicate readConfig call per direct dep.
328
+ const spmConfig = spmConfigOf(current.root, readConfig(current.root));
289
329
  if (current.swiftName == null) {
290
- current.swiftName = resolveName(currentName, config);
330
+ const resolved = resolveSwiftName(
331
+ currentName,
332
+ spmConfig,
333
+ podspecFor(current.root, current.platforms.ios.podspecPath),
334
+ );
335
+ current.swiftName = resolved.name;
336
+ current.swiftNameSource = resolved.source;
337
+ current.swiftNamePodspecKey = resolved.podspecKey;
291
338
  }
292
- // $FlowFixMe[prop-missing] config has dynamic shape
293
- const transitives /*: Array<string> */ = config?.spm?.dependencies ?? [];
339
+ const transitives = stringList(spmConfig?.dependencies);
294
340
 
295
341
  const currentSpmDeps /*: Array<string> */ = [];
296
342
  for (const transitiveName of transitives) {
@@ -298,7 +344,7 @@ function expandSpmDependencies(
298
344
  const transitiveRoot = resolveDep(transitiveName, current.root);
299
345
  if (transitiveRoot == null) {
300
346
  throw new Error(
301
- `react-native autolinking: '${currentName}' declares an unresolvable spm.dependency '${transitiveName}'. Ensure '${transitiveName}' is installed and visible via Node module resolution from ${current.root}.`,
347
+ `react-native autolinking: '${currentName}' declares an unresolvable SwiftPM dependency '${transitiveName}'. Ensure '${transitiveName}' is installed and visible via Node module resolution from ${current.root}.`,
302
348
  );
303
349
  }
304
350
 
@@ -312,11 +358,18 @@ function expandSpmDependencies(
312
358
  continue;
313
359
  }
314
360
 
361
+ const resolved = resolveSwiftName(
362
+ transitiveName,
363
+ spmConfigOf(transitiveRoot, transitiveConfig),
364
+ podspecFor(transitiveRoot, iosPlatform.podspecPath),
365
+ );
315
366
  byName.set(transitiveName, {
316
367
  name: transitiveName,
317
368
  root: transitiveRoot,
318
369
  platforms: {ios: iosPlatform},
319
- swiftName: resolveName(transitiveName, transitiveConfig),
370
+ swiftName: resolved.name,
371
+ swiftNameSource: resolved.source,
372
+ swiftNamePodspecKey: resolved.podspecKey,
320
373
  spmDependencies: [],
321
374
  });
322
375
  queue.push(transitiveName);
@@ -328,37 +381,26 @@ function expandSpmDependencies(
328
381
 
329
382
  const allDeps /*: Array<AutolinkedDep> */ = Array.from(byName.values());
330
383
 
331
- disambiguateSharedSwiftNames(allDeps, autoNamed, log);
332
-
333
- // Both checks below validate the FINAL set, after that pass: a borrowed scope
334
- // can land on a reserved name, or on one another dep already holds.
335
384
  assertNoReservedSwiftNames(allDeps, reserved);
336
385
 
337
- // Collision check: two deps mapping to the same Swift name (whether via
338
- // override or auto-derivation) would clobber each other in the synth
339
- // package layout and the centralized headers tree. Surface it now with a
340
- // clear message instead of letting SPM emit a confusing duplicate-target
341
- // error later.
342
- // Key case-INSENSITIVELY: resolveSwiftName permits lowercase ('worklets')
343
- // while toSwiftName produces TitleCase ('Worklets') — an exact-equality check
344
- // passes but the two still collide as directories on the default
345
- // case-insensitive macOS filesystem (synth package layout + headers tree).
386
+ // Collision check: two deps mapping to the same Swift name would clobber each
387
+ // other in the synth package layout and the centralized headers tree. Surface
388
+ // it now with a clear message instead of letting SPM emit a confusing
389
+ // duplicate-target error later. swiftNameKey is what makes two names two
390
+ // targets — punctuation collapses into one module, case into one directory.
346
391
  const seen /*: Map<string, {name: string, swiftName: string}> */ = new Map();
347
392
  for (const dep of allDeps) {
348
393
  const swiftName = dep.swiftName;
349
394
  if (swiftName == null) {
350
395
  continue;
351
396
  }
352
- const key = swiftName.toLowerCase();
397
+ const key = swiftNameKey(swiftName);
353
398
  const existing = seen.get(key);
354
399
  if (existing != null) {
355
- const same = existing.swiftName === swiftName;
356
400
  throw new SpmNameCollisionError(
357
401
  `react-native autolinking: SPM Swift name collision: '${existing.name}' ('${existing.swiftName}') and '${dep.name}' ('${swiftName}') ` +
358
- (same
359
- ? `both resolve to '${swiftName}'.`
360
- : `differ only in case, which collides on case-insensitive filesystems.`) +
361
- ` Set a distinct 'spm.name' in one of their react-native.config.js files.`,
402
+ collisionDiagnosis(existing.swiftName, swiftName) +
403
+ ` Set a distinct 'swiftpmConfig.name' in one of their package.json files.`,
362
404
  );
363
405
  }
364
406
  seen.set(key, {name: dep.name, swiftName});
@@ -424,12 +466,31 @@ function defaultResolveDep(
424
466
  }
425
467
  }
426
468
 
469
+ function defaultReadPodspec(
470
+ root /*: string */,
471
+ podspecPath /*: ?string */,
472
+ ) /*: ?PodspecFacts */ {
473
+ // Deps synthesized from declared dependencies carry no podspecPath — only
474
+ // autolinking.json records one — so the dep root is searched as well.
475
+ const found = podspecPath ?? findPodspecs(root)[0];
476
+ if (found == null) {
477
+ return null;
478
+ }
479
+ try {
480
+ // Ruby-computed fields need the real evaluator; everything else is spared
481
+ // the `pod ipc spec` spawn.
482
+ return readPodspecNames(found) ?? readPodspecCached(found);
483
+ } catch {
484
+ return null;
485
+ }
486
+ }
487
+
427
488
  module.exports = {
428
489
  SpmNameCollisionError,
429
490
  assertSwiftNameNotReserved,
430
491
  expandSpmDependencies,
431
- isValidSwiftName,
432
492
  resolveSwiftName,
433
493
  defaultReadConfig,
494
+ defaultReadPodspec,
434
495
  defaultResolveDep,
435
496
  };