react-native 0.87.0-rc.1 → 0.87.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.
- package/Libraries/Core/InitializeCore.js +8 -1
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/ReactNative/AppRegistry.flow.js +1 -0
- package/Libraries/ReactNative/AppRegistryImpl.js +2 -3
- package/React/Base/RCTVersion.m +1 -1
- package/React/I18n/RCTLocalizedString.mm +38 -2
- package/React-Core-prebuilt.podspec +45 -17
- package/React-Core.podspec +9 -2
- package/ReactAndroid/external-artifacts/build.gradle.kts +49 -0
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SynchronousMountItem.kt +8 -2
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
- package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
- package/package.json +10 -8
- package/react-native.config.js +81 -0
- package/scripts/cocoapods/fabric.rb +1 -1
- package/scripts/cocoapods/rncore.rb +35 -78
- package/scripts/cocoapods/rncore_facades.rb +232 -0
- package/scripts/cocoapods/rndependencies.rb +64 -3
- package/scripts/cocoapods/rndeps_facades.rb +193 -0
- package/scripts/cocoapods/spm.rb +78 -11
- package/scripts/codegen/templates/Package.swift.spm-template +97 -0
- package/scripts/react-native-xcode.sh +20 -0
- package/scripts/react_native_pods.rb +65 -16
- package/scripts/replace-rncore-version.js +53 -6
- package/scripts/setup-apple-spm.js +1165 -0
- package/scripts/spm/__doc__/rfc-spm-xcframework.md +707 -0
- package/scripts/spm/__doc__/spm-autolinking-plugins.md +244 -0
- package/scripts/spm/__doc__/spm-header-paths-contract.md +97 -0
- package/scripts/spm/__doc__/spm-plugins-assessment.md +128 -0
- package/scripts/spm/__doc__/spm-scripts.md +451 -0
- package/scripts/spm/autolinking-plugins.js +331 -0
- package/scripts/spm/download-spm-artifacts.js +1409 -0
- package/scripts/spm/expand-spm-dependencies.js +216 -0
- package/scripts/spm/flavored-frameworks.js +1008 -0
- package/scripts/spm/generate-spm-autolinking-config.js +161 -0
- package/scripts/spm/generate-spm-autolinking.js +1888 -0
- package/scripts/spm/generate-spm-package.js +302 -0
- package/scripts/spm/generate-spm-xcodeproj.js +2224 -0
- package/scripts/spm/read-podspec.js +695 -0
- package/scripts/spm/scaffold-package-swift.js +1206 -0
- package/scripts/spm/spm-pbxproj.js +654 -0
- package/scripts/spm/spm-types.js +517 -0
- package/scripts/spm/spm-utils.js +645 -0
- package/scripts/spm/sync-spm-autolinking.js +160 -0
- package/sdks/.hermesv1version +1 -0
- package/sdks/hermes-engine/utils/replace_hermes_version.js +18 -4
- package/sdks/hermes-engine/version.properties +1 -1
- package/third-party-podspecs/ReactNativeDependencies.podspec +2 -2
- package/types_generated/Libraries/ReactNative/AppRegistry.flow.d.ts +2 -2
- package/Libraries/Utilities/SceneTracker.js +0 -42
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* SwiftPM autolinking plugins — the extension seam for frameworks (Expo first)
|
|
13
|
+
* that need to contribute their own SwiftPM package refs / product deps /
|
|
14
|
+
* generated sources into the autolinked package graph.
|
|
15
|
+
*
|
|
16
|
+
* PREVIEW / UNSTABLE CONTRACT. The discovery mechanism and the plugin
|
|
17
|
+
* function's context/return shape may change while the first real consumers
|
|
18
|
+
* (Expo) validate it.
|
|
19
|
+
*
|
|
20
|
+
* Why a plugin and not a post-process: the Xcode auto-sync build phase
|
|
21
|
+
* re-runs autolinking on every dependency change, so a one-shot rewrite of the
|
|
22
|
+
* generated Package.swift is clobbered on the next sync. Plugins are invoked
|
|
23
|
+
* from generate-spm-autolinking.js:main() — the single function both `add`/
|
|
24
|
+
* `update` and the build-time `sync` call — so a contribution runs on EVERY
|
|
25
|
+
* regeneration.
|
|
26
|
+
*
|
|
27
|
+
* Discovery (transitive, mirrors CocoaPods' `use_expo_modules!`): any
|
|
28
|
+
* autolinked dependency can register a plugin from its own
|
|
29
|
+
* react-native.config.js —
|
|
30
|
+
*
|
|
31
|
+
* // node_modules/expo/react-native.config.js
|
|
32
|
+
* module.exports = { spm: { autolinkingPlugin: './spm/plugin.js' } };
|
|
33
|
+
*
|
|
34
|
+
* Installing the framework is enough; no app-level config is required. An app
|
|
35
|
+
* MAY still exclude a plugin via `spm.denyPlugins` (npm-name list) in its own
|
|
36
|
+
* react-native.config.js — an escape hatch, not a required allowlist.
|
|
37
|
+
*
|
|
38
|
+
* Contract:
|
|
39
|
+
*
|
|
40
|
+
* module.exports = function plugin(context) {
|
|
41
|
+
* // context: {appRoot, projectRoot, reactNativeRoot, autolinking,
|
|
42
|
+
* // outputDir, react}
|
|
43
|
+
* // context.react (?ReactDescriptor): how to depend on React —
|
|
44
|
+
* // {packageRef, products}. packageRef is local ({name, path (absolute),
|
|
45
|
+
* // relPath}) or remote ({name, url, version}); products is the set RN
|
|
46
|
+
* // gives its own autolinked targets (incl. ReactAppHeaders from the
|
|
47
|
+
* // separate React-GeneratedCode package), filtered to what resolves
|
|
48
|
+
* // this run. Use it instead of re-deriving RN's path/identity/products.
|
|
49
|
+
* return {
|
|
50
|
+
* packageDependencies: [{name, path}] | [{name, url, version}],
|
|
51
|
+
* productDependencies: [{name, package}],
|
|
52
|
+
* // Sources compiled INTO THE APP TARGET (not the static Autolinked
|
|
53
|
+
* // aggregate) — the only place an @objc class reaches the ObjC
|
|
54
|
+
* // classlist, so emit files here when a class must be reachable by
|
|
55
|
+
* // name (e.g. via NSClassFromString), as Expo's ExpoModulesProvider
|
|
56
|
+
* // needs. Recorded to `.spm-plugin-generated-sources.json`; the
|
|
57
|
+
* // `spm add`/`update` xcodeproj injector wires each into the app
|
|
58
|
+
* // target (PBXFileReference + Sources-phase entry), marker-tracked
|
|
59
|
+
* // so deinit reverts and update reconciles.
|
|
60
|
+
* generatedSources: [{path}],
|
|
61
|
+
* // Precompiled dynamic frameworks selected and embedded by RN outside
|
|
62
|
+
* // the SwiftPM graph. Both flavors are mandatory.
|
|
63
|
+
* flavoredFrameworks: [{id, frameworkName, linkage: 'dynamic',
|
|
64
|
+
* flavors: {debug, release}}],
|
|
65
|
+
* // Absolute paths (dirs OR files) the Xcode auto-sync build phase
|
|
66
|
+
* // should watch for staleness — e.g. the plugin dep's own
|
|
67
|
+
* // `Package.swift`, `expo-module.config.json`, and per-module
|
|
68
|
+
* // manifests. Folded into `.spm-sync-watch-paths`; a file edit or a
|
|
69
|
+
* // dir add/remove there re-triggers the sync. Relative/empty/non-string
|
|
70
|
+
* // entries are dropped with a warning; a non-array is ignored.
|
|
71
|
+
* watchPaths: ['/abs/path/Package.swift', '/abs/dir'],
|
|
72
|
+
* };
|
|
73
|
+
* };
|
|
74
|
+
*
|
|
75
|
+
* The plugin returns DATA; it never writes into RN's generated tree. RN owns
|
|
76
|
+
* the merge, so regeneration stays deterministic and idempotent.
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
const path = require('path');
|
|
80
|
+
|
|
81
|
+
/*:: import type {
|
|
82
|
+
AutolinkedDep,
|
|
83
|
+
PluginContext,
|
|
84
|
+
PluginResult,
|
|
85
|
+
DiscoveredPlugin,
|
|
86
|
+
} from './spm-types';
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Discover plugins declared by autolinked deps. `readConfig(root)` returns the
|
|
91
|
+
* dep's parsed react-native.config.js (or null). `denyList` is the app's
|
|
92
|
+
* `spm.denyPlugins` (npm names to skip). Fail-closed: a declared-but-missing
|
|
93
|
+
* or unloadable plugin throws, naming the dep — a framework silently dropping
|
|
94
|
+
* its modules is worse than a loud stop.
|
|
95
|
+
*/
|
|
96
|
+
function discoverPlugins(
|
|
97
|
+
deps /*: ReadonlyArray<AutolinkedDep> */,
|
|
98
|
+
readConfig /*: (root: string) => ?{readonly [string]: unknown} */,
|
|
99
|
+
denyList /*: ReadonlyArray<string> */ = [],
|
|
100
|
+
) /*: Array<DiscoveredPlugin> */ {
|
|
101
|
+
const denied = new Set(denyList);
|
|
102
|
+
const found /*: Array<DiscoveredPlugin> */ = [];
|
|
103
|
+
for (const dep of deps) {
|
|
104
|
+
if (denied.has(dep.name)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const config = readConfig(dep.root);
|
|
108
|
+
// $FlowFixMe[incompatible-use] config has a dynamic shape
|
|
109
|
+
const rel = config?.spm?.autolinkingPlugin;
|
|
110
|
+
if (rel == null) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (typeof rel !== 'string' || rel.length === 0) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`react-native spm: '${dep.name}' declares an invalid spm.autolinkingPlugin ` +
|
|
116
|
+
`(expected a module path string).`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const pluginPath = path.resolve(dep.root, rel);
|
|
120
|
+
let fn: unknown;
|
|
121
|
+
try {
|
|
122
|
+
// $FlowFixMe[unsupported-syntax] dynamic require by computed path
|
|
123
|
+
fn = require(pluginPath);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`react-native spm: failed to load the autolinking plugin for '${dep.name}' ` +
|
|
127
|
+
`at ${pluginPath}: ${e.message}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
// Support `module.exports = fn` and `{default: fn}` / `{plugin: fn}`.
|
|
131
|
+
const resolved =
|
|
132
|
+
typeof fn === 'function'
|
|
133
|
+
? fn
|
|
134
|
+
: // $FlowFixMe[incompatible-use] interop shapes
|
|
135
|
+
typeof fn?.default === 'function'
|
|
136
|
+
? fn.default
|
|
137
|
+
: // $FlowFixMe[incompatible-use]
|
|
138
|
+
typeof fn?.plugin === 'function'
|
|
139
|
+
? fn.plugin
|
|
140
|
+
: null;
|
|
141
|
+
if (resolved == null) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`react-native spm: the autolinking plugin for '${dep.name}' at ${pluginPath} ` +
|
|
144
|
+
`does not export a function.`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
found.push({depName: dep.name, pluginPath, plugin: resolved});
|
|
148
|
+
}
|
|
149
|
+
return found;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Invoke discovered plugins and merge their results. Each plugin gets the same
|
|
154
|
+
* context. Fail-closed: a throwing plugin aborts (named), and a malformed
|
|
155
|
+
* return is rejected. Dedupe package + product deps by name so a plugin and an
|
|
156
|
+
* npm dep that both reference the same package don't double-declare.
|
|
157
|
+
*/
|
|
158
|
+
function invokePlugins(
|
|
159
|
+
plugins /*: ReadonlyArray<DiscoveredPlugin> */,
|
|
160
|
+
context /*: PluginContext */,
|
|
161
|
+
logger /*: {warn: (msg: string) => void} */ = {
|
|
162
|
+
warn: msg => console.warn(msg),
|
|
163
|
+
},
|
|
164
|
+
) /*: PluginResult */ {
|
|
165
|
+
const packageDependencies /*: Array<{name: string, path?: string, url?: string, version?: string}> */ =
|
|
166
|
+
[];
|
|
167
|
+
const productDependencies /*: Array<{name: string, package: string}> */ = [];
|
|
168
|
+
const generatedSources /*: Array<{path: string}> */ = [];
|
|
169
|
+
const flavoredFrameworks /*: Array<{id: string, frameworkName: string, linkage: 'dynamic', flavors: {debug: string, release: string}}> */ =
|
|
170
|
+
[];
|
|
171
|
+
const watchPaths /*: Array<string> */ = [];
|
|
172
|
+
const seenPackages /*: Set<string> */ = new Set();
|
|
173
|
+
const seenProducts /*: Set<string> */ = new Set();
|
|
174
|
+
const seenFrameworkIds /*: Set<string> */ = new Set();
|
|
175
|
+
const seenFrameworkNames /*: Set<string> */ = new Set();
|
|
176
|
+
|
|
177
|
+
for (const {depName, pluginPath, plugin} of plugins) {
|
|
178
|
+
let result: unknown;
|
|
179
|
+
try {
|
|
180
|
+
result = plugin(context);
|
|
181
|
+
} catch (e) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`react-native spm: the autolinking plugin for '${depName}' (${pluginPath}) ` +
|
|
184
|
+
`threw: ${e.message}`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (result == null) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (typeof result !== 'object') {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`react-native spm: the autolinking plugin for '${depName}' returned a ` +
|
|
193
|
+
`${typeof result}; expected an object or undefined.`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
// $FlowFixMe[incompatible-use] validated field-by-field below
|
|
197
|
+
const pkgs = result.packageDependencies ?? [];
|
|
198
|
+
// $FlowFixMe[incompatible-use]
|
|
199
|
+
const prods = result.productDependencies ?? [];
|
|
200
|
+
// $FlowFixMe[incompatible-use]
|
|
201
|
+
const srcs = result.generatedSources ?? [];
|
|
202
|
+
// $FlowFixMe[incompatible-use]
|
|
203
|
+
const rawFrameworks = result.flavoredFrameworks ?? [];
|
|
204
|
+
// $FlowFixMe[incompatible-use]
|
|
205
|
+
const rawWatch = result.watchPaths ?? [];
|
|
206
|
+
if (!Array.isArray(rawFrameworks)) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`react-native spm: '${depName}' returned a non-array flavoredFrameworks.`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
// Watch paths are best-effort staleness hints, so
|
|
212
|
+
// a non-array is ignored (warn, never fatal).
|
|
213
|
+
if (!Array.isArray(rawWatch)) {
|
|
214
|
+
logger.warn(
|
|
215
|
+
`react-native spm: '${depName}' returned a non-array watchPaths ` +
|
|
216
|
+
`— ignoring it.`,
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
const watch = Array.isArray(rawWatch) ? rawWatch : [];
|
|
220
|
+
for (const p of pkgs) {
|
|
221
|
+
if (p == null || typeof p.name !== 'string') {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`react-native spm: '${depName}' returned a packageDependency without a name.`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
if (p.path == null && (p.url == null || p.version == null)) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`react-native spm: '${depName}' packageDependency '${p.name}' needs either ` +
|
|
229
|
+
`a path or a url+version.`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
if (!seenPackages.has(p.name)) {
|
|
233
|
+
seenPackages.add(p.name);
|
|
234
|
+
packageDependencies.push(p);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
for (const p of prods) {
|
|
238
|
+
if (
|
|
239
|
+
p == null ||
|
|
240
|
+
typeof p.name !== 'string' ||
|
|
241
|
+
typeof p.package !== 'string'
|
|
242
|
+
) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`react-native spm: '${depName}' returned a productDependency needing name + package.`,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
const key = `${p.package}/${p.name}`;
|
|
248
|
+
if (!seenProducts.has(key)) {
|
|
249
|
+
seenProducts.add(key);
|
|
250
|
+
productDependencies.push(p);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
for (const s of srcs) {
|
|
254
|
+
if (s == null || typeof s.path !== 'string') {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`react-native spm: '${depName}' returned a generatedSource without a path.`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
generatedSources.push(s);
|
|
260
|
+
}
|
|
261
|
+
for (const framework of rawFrameworks) {
|
|
262
|
+
if (
|
|
263
|
+
framework == null ||
|
|
264
|
+
typeof framework.id !== 'string' ||
|
|
265
|
+
!/^[A-Za-z0-9_.-]+$/.test(framework.id) ||
|
|
266
|
+
typeof framework.frameworkName !== 'string' ||
|
|
267
|
+
framework.frameworkName.length === 0 ||
|
|
268
|
+
framework.linkage !== 'dynamic' ||
|
|
269
|
+
framework.flavors == null ||
|
|
270
|
+
typeof framework.flavors.debug !== 'string' ||
|
|
271
|
+
typeof framework.flavors.release !== 'string' ||
|
|
272
|
+
!path.isAbsolute(framework.flavors.debug) ||
|
|
273
|
+
!path.isAbsolute(framework.flavors.release)
|
|
274
|
+
) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`react-native spm: '${depName}' returned an invalid flavoredFramework ` +
|
|
277
|
+
'(need {id, frameworkName, linkage: "dynamic", flavors: ' +
|
|
278
|
+
'{debug, release}} with absolute flavor paths).',
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
if (seenFrameworkIds.has(framework.id)) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`react-native spm: duplicate flavored framework id '${framework.id}'.`,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (seenFrameworkNames.has(framework.frameworkName)) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
`react-native spm: multiple plugins embed '${framework.frameworkName}.framework'.`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
seenFrameworkIds.add(framework.id);
|
|
292
|
+
seenFrameworkNames.add(framework.frameworkName);
|
|
293
|
+
flavoredFrameworks.push({
|
|
294
|
+
id: framework.id,
|
|
295
|
+
frameworkName: framework.frameworkName,
|
|
296
|
+
linkage: 'dynamic',
|
|
297
|
+
flavors: {
|
|
298
|
+
debug: framework.flavors.debug,
|
|
299
|
+
release: framework.flavors.release,
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
// watchPaths are dropped (with a warning), not fatal. A non-string, empty,
|
|
304
|
+
// or relative entry only
|
|
305
|
+
// means one staleness input is missed, not a broken build. Absolute-only so
|
|
306
|
+
// the generated phase (which has no cwd context) can test them directly.
|
|
307
|
+
for (const w of watch) {
|
|
308
|
+
if (typeof w !== 'string' || w.length === 0 || !path.isAbsolute(w)) {
|
|
309
|
+
logger.warn(
|
|
310
|
+
`react-native spm: '${depName}' returned an invalid watchPath ` +
|
|
311
|
+
`(need a non-empty absolute path string) — dropping it.`,
|
|
312
|
+
);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
watchPaths.push(w);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
packageDependencies,
|
|
321
|
+
productDependencies,
|
|
322
|
+
generatedSources,
|
|
323
|
+
flavoredFrameworks,
|
|
324
|
+
watchPaths,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
module.exports = {
|
|
329
|
+
discoverPlugins,
|
|
330
|
+
invokePlugins,
|
|
331
|
+
};
|