react-native 0.87.0-rc.0 → 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 +9 -1
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/ReactNative/AppRegistry.flow.js +1 -0
- package/Libraries/ReactNative/AppRegistryImpl.js +2 -3
- package/Libraries/ReactPrivate/ReactNativePrivateInterface.js +5 -116
- package/Libraries/ReactPrivate/ReactNativePrivateInterface.js.flow +5 -31
- 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/index.js +15 -3
- package/index.js.flow +0 -2
- package/package.json +27 -29
- 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/src/asset-registry.js +1 -1
- package/src/react-private-interface.js +145 -0
- package/src/react-private-interface.js.flow +48 -0
- package/src/setup-env.js +22 -0
- package/src/unstable-internals-do-not-use.d.ts +214 -0
- package/src/unstable-internals-do-not-use.js +76 -0
- package/third-party-podspecs/ReactNativeDependencies.podspec +2 -2
- package/types_generated/Libraries/ReactNative/AppRegistry.flow.d.ts +2 -2
- package/types_generated/Libraries/ReactPrivate/ReactNativePrivateInterface.d.ts +6 -21
- package/types_generated/index.d.ts +1 -2
- package/types_generated/src/private/renderer/events/dispatchNativeEvent.d.ts +26 -0
- package/types_generated/src/react-private-interface.d.ts +33 -0
- package/Libraries/Utilities/SceneTracker.js +0 -42
- package/jest-preset.js +0 -26
- package/rn-get-polyfills.js +0 -13
- package/types/tsconfig.json +0 -17
- package/types_generated/Libraries/Components/Touchable/Touchable.d.ts +0 -261
- package/types_generated/tsconfig.test.json +0 -17
|
@@ -0,0 +1,1008 @@
|
|
|
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 strict-local
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const {execFileSync} = require('child_process');
|
|
14
|
+
const crypto = require('crypto');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
// Info.plist parsing goes through plutil (as the pre-B1 swap script did): the
|
|
19
|
+
// `plist` npm module is NOT a react-native dependency, so it only resolves
|
|
20
|
+
// inside this monorepo via hoisting — requiring it breaks `spm add` in a
|
|
21
|
+
// fresh consumer app with "Cannot find module 'plist'".
|
|
22
|
+
function plistJson(p /*: string */) /*: $FlowFixMe */ {
|
|
23
|
+
return JSON.parse(
|
|
24
|
+
execFileSync('plutil', ['-convert', 'json', '-o', '-', p]).toString(),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/*:: import type {
|
|
29
|
+
FlavoredFrameworkManifestEntry,
|
|
30
|
+
FlavoredFrameworksManifest,
|
|
31
|
+
ParsedXcframework,
|
|
32
|
+
PluginFlavoredFramework,
|
|
33
|
+
XcframeworkSlice,
|
|
34
|
+
} from './spm-types'; */
|
|
35
|
+
|
|
36
|
+
/*::
|
|
37
|
+
type ArtifactIndexEntry = {xcframeworkPath: string, url?: string};
|
|
38
|
+
type ArtifactIndex = {[string]: ArtifactIndexEntry};
|
|
39
|
+
type ValidatedFramework = {
|
|
40
|
+
id: string,
|
|
41
|
+
frameworkName: string,
|
|
42
|
+
executableName: string,
|
|
43
|
+
linkage: 'dynamic',
|
|
44
|
+
slices: Array<XcframeworkSlice>,
|
|
45
|
+
};
|
|
46
|
+
type StagedFramework = {
|
|
47
|
+
id: string,
|
|
48
|
+
frameworkName: string,
|
|
49
|
+
executableName: string,
|
|
50
|
+
linkage: 'dynamic',
|
|
51
|
+
artifactRelativePath: string,
|
|
52
|
+
slices: Array<XcframeworkSlice>,
|
|
53
|
+
sourcePaths: {debug: string, release: string},
|
|
54
|
+
};
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
const FLAVORS /*: ReadonlyArray<'debug' | 'release'> */ = ['debug', 'release'];
|
|
58
|
+
|
|
59
|
+
const FLAVORED_FRAMEWORKS_MANIFEST = 'flavored-frameworks.json';
|
|
60
|
+
const PENDING_ARTIFACT_STAMP = '.artifact-stamp.pending';
|
|
61
|
+
const PLUGIN_FRAMEWORKS_MANIFEST /*: string */ = path.join(
|
|
62
|
+
'build',
|
|
63
|
+
'generated',
|
|
64
|
+
'autolinking',
|
|
65
|
+
'.spm-plugin-flavored-frameworks.json',
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const BUILTIN_FRAMEWORKS = [
|
|
69
|
+
{id: 'react', artifactName: 'React'},
|
|
70
|
+
{id: 'react-native-dependencies', artifactName: 'ReactNativeDependencies'},
|
|
71
|
+
{id: 'hermes', artifactName: 'hermes-engine'},
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const INVARIANT_BINARY_TARGETS = [
|
|
75
|
+
'ReactNativeHeaders',
|
|
76
|
+
'ReactNativeDependenciesHeaders',
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
function readJson(filePath /*: string */) /*: unknown */ {
|
|
80
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function requireString(
|
|
84
|
+
value /*: unknown */,
|
|
85
|
+
label /*: string */,
|
|
86
|
+
) /*: string */ {
|
|
87
|
+
if (typeof value !== 'string') {
|
|
88
|
+
throw new Error(`${label} must be a string`);
|
|
89
|
+
}
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function readArtifactIndex(artifactsDir /*: string */) /*: ArtifactIndex */ {
|
|
94
|
+
const indexPath = path.join(artifactsDir, 'artifacts.json');
|
|
95
|
+
if (!fs.existsSync(indexPath)) {
|
|
96
|
+
throw new Error(`artifacts.json missing in ${artifactsDir}`);
|
|
97
|
+
}
|
|
98
|
+
const parsed = readJson(indexPath);
|
|
99
|
+
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
100
|
+
throw new Error(`artifacts.json in ${artifactsDir} is not an object`);
|
|
101
|
+
}
|
|
102
|
+
const result /*: ArtifactIndex */ = {};
|
|
103
|
+
for (const name of Object.keys(parsed)) {
|
|
104
|
+
const entry = parsed[name];
|
|
105
|
+
if (
|
|
106
|
+
entry == null ||
|
|
107
|
+
typeof entry !== 'object' ||
|
|
108
|
+
typeof entry.xcframeworkPath !== 'string'
|
|
109
|
+
) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`artifacts.json entry '${name}' in ${artifactsDir} is invalid`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
result[name] = {
|
|
115
|
+
xcframeworkPath: entry.xcframeworkPath,
|
|
116
|
+
...(typeof entry.url === 'string' ? {url: entry.url} : {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function frameworkNameFromLibraryPath(libraryPath /*: string */) /*: string */ {
|
|
123
|
+
if (!libraryPath.endsWith('.framework')) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`flavored binary '${libraryPath}' is not a dynamic framework; ` +
|
|
126
|
+
'only .framework XCFramework libraries are supported',
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return path.basename(libraryPath, '.framework');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function sdkConditionForSlice(
|
|
133
|
+
platform /*: string */,
|
|
134
|
+
variant /*: ?string */,
|
|
135
|
+
) /*: string */ {
|
|
136
|
+
if (platform === 'ios' && variant === 'simulator') {
|
|
137
|
+
return 'iphonesimulator*';
|
|
138
|
+
}
|
|
139
|
+
if (platform === 'ios' && variant === 'maccatalyst') {
|
|
140
|
+
return 'macosx*';
|
|
141
|
+
}
|
|
142
|
+
if (platform === 'ios') {
|
|
143
|
+
return 'iphoneos*';
|
|
144
|
+
}
|
|
145
|
+
if (platform === 'tvos' && variant === 'simulator') {
|
|
146
|
+
return 'appletvsimulator*';
|
|
147
|
+
}
|
|
148
|
+
if (platform === 'tvos') {
|
|
149
|
+
return 'appletvos*';
|
|
150
|
+
}
|
|
151
|
+
if (platform === 'xros' && variant === 'simulator') {
|
|
152
|
+
return 'xrsimulator*';
|
|
153
|
+
}
|
|
154
|
+
if (platform === 'xros') {
|
|
155
|
+
return 'xros*';
|
|
156
|
+
}
|
|
157
|
+
if (platform === 'macos') {
|
|
158
|
+
return 'macosx*';
|
|
159
|
+
}
|
|
160
|
+
throw new Error(
|
|
161
|
+
`unsupported XCFramework platform '${platform}'` +
|
|
162
|
+
(variant != null ? ` variant '${variant}'` : ''),
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function parseXcframework(
|
|
167
|
+
xcframeworkPath /*: string */,
|
|
168
|
+
) /*: ParsedXcframework */ {
|
|
169
|
+
const infoPath = path.join(xcframeworkPath, 'Info.plist');
|
|
170
|
+
if (!fs.existsSync(infoPath)) {
|
|
171
|
+
throw new Error(`XCFramework Info.plist missing at ${infoPath}`);
|
|
172
|
+
}
|
|
173
|
+
const info = plistJson(infoPath);
|
|
174
|
+
const libraries = info.AvailableLibraries;
|
|
175
|
+
if (!Array.isArray(libraries) || libraries.length === 0) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`XCFramework has no AvailableLibraries: ${xcframeworkPath}`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
let frameworkName = null;
|
|
181
|
+
let executableName = null;
|
|
182
|
+
const slices = libraries.map(library => {
|
|
183
|
+
const identifier = library.LibraryIdentifier;
|
|
184
|
+
const libraryPath = library.LibraryPath;
|
|
185
|
+
const platform = library.SupportedPlatform;
|
|
186
|
+
const variant = library.SupportedPlatformVariant ?? null;
|
|
187
|
+
const architectures = library.SupportedArchitectures;
|
|
188
|
+
if (
|
|
189
|
+
typeof identifier !== 'string' ||
|
|
190
|
+
typeof libraryPath !== 'string' ||
|
|
191
|
+
typeof platform !== 'string' ||
|
|
192
|
+
!Array.isArray(architectures) ||
|
|
193
|
+
!architectures.every(a => typeof a === 'string')
|
|
194
|
+
) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`XCFramework contains an invalid library entry: ${xcframeworkPath}`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const sliceFrameworkName = frameworkNameFromLibraryPath(libraryPath);
|
|
200
|
+
const binaryPath =
|
|
201
|
+
typeof library.BinaryPath === 'string'
|
|
202
|
+
? library.BinaryPath
|
|
203
|
+
: path.join(libraryPath, sliceFrameworkName);
|
|
204
|
+
const sliceExecutableName = path.basename(binaryPath);
|
|
205
|
+
if (frameworkName == null) {
|
|
206
|
+
frameworkName = sliceFrameworkName;
|
|
207
|
+
executableName = sliceExecutableName;
|
|
208
|
+
} else if (
|
|
209
|
+
frameworkName !== sliceFrameworkName ||
|
|
210
|
+
executableName !== sliceExecutableName
|
|
211
|
+
) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
`XCFramework changes framework or executable name between slices: ${xcframeworkPath}`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
sdk: sdkConditionForSlice(platform, variant),
|
|
218
|
+
platform,
|
|
219
|
+
variant,
|
|
220
|
+
architectures: [...architectures].sort(),
|
|
221
|
+
libraryIdentifier: identifier,
|
|
222
|
+
libraryPath,
|
|
223
|
+
binaryPath,
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
if (frameworkName == null || executableName == null) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`could not identify framework binary in ${xcframeworkPath}`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
slices.sort((a, b) =>
|
|
232
|
+
`${a.sdk}:${a.libraryIdentifier}`.localeCompare(
|
|
233
|
+
`${b.sdk}:${b.libraryIdentifier}`,
|
|
234
|
+
),
|
|
235
|
+
);
|
|
236
|
+
return {
|
|
237
|
+
path: xcframeworkPath,
|
|
238
|
+
frameworkName,
|
|
239
|
+
executableName,
|
|
240
|
+
slices,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function sliceShape(parsed /*: ParsedXcframework */) /*: string */ {
|
|
245
|
+
return JSON.stringify(parsed.slices);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function walkFiles(root /*: string */) /*: Array<string> */ {
|
|
249
|
+
const files = [];
|
|
250
|
+
const visit = (dir /*: string */) => {
|
|
251
|
+
for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
|
|
252
|
+
const full = path.join(dir, String(entry.name));
|
|
253
|
+
if (entry.isDirectory()) {
|
|
254
|
+
visit(full);
|
|
255
|
+
} else if (entry.isSymbolicLink()) {
|
|
256
|
+
const stat = fs.statSync(full);
|
|
257
|
+
if (stat.isDirectory()) {
|
|
258
|
+
visit(full);
|
|
259
|
+
} else {
|
|
260
|
+
files.push(full);
|
|
261
|
+
}
|
|
262
|
+
} else if (entry.isFile()) {
|
|
263
|
+
files.push(full);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
if (fs.existsSync(root) && fs.statSync(root).isFile()) {
|
|
268
|
+
files.push(root);
|
|
269
|
+
} else if (fs.existsSync(root)) {
|
|
270
|
+
visit(root);
|
|
271
|
+
}
|
|
272
|
+
return files.sort();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function hashTrees(roots /*: Array<string> */) /*: string */ {
|
|
276
|
+
const hash = crypto.createHash('sha256');
|
|
277
|
+
for (const root of roots) {
|
|
278
|
+
const realRoot = fs.realpathSync(root);
|
|
279
|
+
for (const file of walkFiles(realRoot)) {
|
|
280
|
+
hash.update(path.relative(realRoot, file));
|
|
281
|
+
hash.update('\0');
|
|
282
|
+
hash.update(fs.readFileSync(file));
|
|
283
|
+
hash.update('\0');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return hash.digest('hex');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function frameworkHeaderHashes(
|
|
290
|
+
parsed /*: ParsedXcframework */,
|
|
291
|
+
) /*: Array<string> */ {
|
|
292
|
+
return parsed.slices.map(slice => {
|
|
293
|
+
const framework = path.join(
|
|
294
|
+
parsed.path,
|
|
295
|
+
slice.libraryIdentifier,
|
|
296
|
+
slice.libraryPath,
|
|
297
|
+
);
|
|
298
|
+
const roots = [];
|
|
299
|
+
const headers = path.join(framework, 'Headers');
|
|
300
|
+
const moduleMap = path.join(framework, 'Modules', 'module.modulemap');
|
|
301
|
+
if (fs.existsSync(headers)) {
|
|
302
|
+
roots.push(headers);
|
|
303
|
+
}
|
|
304
|
+
if (fs.existsSync(moduleMap)) {
|
|
305
|
+
roots.push(moduleMap);
|
|
306
|
+
}
|
|
307
|
+
return roots.length === 0 ? '' : hashTrees(roots);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function invariantHeadersHashes(
|
|
312
|
+
xcframeworkPath /*: string */,
|
|
313
|
+
) /*: Array<string> */ {
|
|
314
|
+
const info = plistJson(path.join(xcframeworkPath, 'Info.plist'));
|
|
315
|
+
if (!Array.isArray(info.AvailableLibraries)) {
|
|
316
|
+
throw new Error(`XCFramework has no libraries: ${xcframeworkPath}`);
|
|
317
|
+
}
|
|
318
|
+
return info.AvailableLibraries.map(library => {
|
|
319
|
+
const identifier = library.LibraryIdentifier;
|
|
320
|
+
const headersPath = library.HeadersPath;
|
|
321
|
+
if (typeof identifier !== 'string' || typeof headersPath !== 'string') {
|
|
322
|
+
throw new Error(
|
|
323
|
+
`headers XCFramework has an invalid slice: ${xcframeworkPath}`,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
return hashTrees([path.join(xcframeworkPath, identifier, headersPath)]);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function assertOneHash(
|
|
331
|
+
label /*: string */,
|
|
332
|
+
hashes /*: Array<string> */,
|
|
333
|
+
) /*: string */ {
|
|
334
|
+
const distinct = Array.from(new Set(hashes));
|
|
335
|
+
if (distinct.length !== 1) {
|
|
336
|
+
throw new Error(`${label} headers differ between XCFramework slices`);
|
|
337
|
+
}
|
|
338
|
+
return distinct[0];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function assertDynamicFramework(parsed /*: ParsedXcframework */) /*: void */ {
|
|
342
|
+
for (const slice of parsed.slices) {
|
|
343
|
+
const binary = path.join(
|
|
344
|
+
parsed.path,
|
|
345
|
+
slice.libraryIdentifier,
|
|
346
|
+
slice.binaryPath,
|
|
347
|
+
);
|
|
348
|
+
if (!fs.existsSync(binary)) {
|
|
349
|
+
throw new Error(
|
|
350
|
+
`${parsed.frameworkName} binary is missing from slice ${slice.libraryIdentifier}: ${binary}`,
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
let output;
|
|
354
|
+
try {
|
|
355
|
+
output = execFileSync('otool', ['-l', binary], {
|
|
356
|
+
encoding: 'utf8',
|
|
357
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
358
|
+
});
|
|
359
|
+
} catch (e) {
|
|
360
|
+
throw new Error(`could not inspect ${binary}: ${e.message}`);
|
|
361
|
+
}
|
|
362
|
+
if (!output.includes('LC_ID_DYLIB')) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
`${parsed.frameworkName} is not a dynamic framework: ${binary}`,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
const frameworkRoot = path.join(
|
|
368
|
+
parsed.path,
|
|
369
|
+
slice.libraryIdentifier,
|
|
370
|
+
slice.libraryPath,
|
|
371
|
+
);
|
|
372
|
+
const nested = walkFiles(frameworkRoot).find(file =>
|
|
373
|
+
file.includes(`${path.sep}Frameworks${path.sep}`),
|
|
374
|
+
);
|
|
375
|
+
if (nested != null) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
`${parsed.frameworkName} contains nested code that RN cannot manage: ${nested}`,
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function validatePair(
|
|
384
|
+
id /*: string */,
|
|
385
|
+
debugPath /*: string */,
|
|
386
|
+
releasePath /*: string */,
|
|
387
|
+
declaredFrameworkName /*: ?string */,
|
|
388
|
+
validateBinaries /*: boolean */,
|
|
389
|
+
) /*: ValidatedFramework */ {
|
|
390
|
+
const debug = parseXcframework(debugPath);
|
|
391
|
+
const release = parseXcframework(releasePath);
|
|
392
|
+
if (
|
|
393
|
+
debug.frameworkName !== release.frameworkName ||
|
|
394
|
+
debug.executableName !== release.executableName ||
|
|
395
|
+
sliceShape(debug) !== sliceShape(release)
|
|
396
|
+
) {
|
|
397
|
+
throw new Error(
|
|
398
|
+
`flavored framework '${id}' has incompatible Debug and Release XCFrameworks`,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
if (
|
|
402
|
+
declaredFrameworkName != null &&
|
|
403
|
+
declaredFrameworkName !== debug.frameworkName
|
|
404
|
+
) {
|
|
405
|
+
throw new Error(
|
|
406
|
+
`flavored framework '${id}' declared '${declaredFrameworkName}' but contains '${debug.frameworkName}'`,
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
// Headers and Swift module interfaces may legitimately differ between SDK
|
|
410
|
+
// slices (for example device vs simulator generated `-Swift.h` files and
|
|
411
|
+
// target-specific `.swiftinterface` names). `sliceShape` above already
|
|
412
|
+
// guarantees a one-to-one slice mapping, so compare each Debug slice with
|
|
413
|
+
// its matching Release slice instead of requiring every slice within one
|
|
414
|
+
// XCFramework to be byte-identical.
|
|
415
|
+
const debugHeaders = frameworkHeaderHashes(debug);
|
|
416
|
+
const releaseHeaders = frameworkHeaderHashes(release);
|
|
417
|
+
if (JSON.stringify(debugHeaders) !== JSON.stringify(releaseHeaders)) {
|
|
418
|
+
throw new Error(
|
|
419
|
+
`flavored framework '${id}' exposes different Debug and Release headers`,
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
if (validateBinaries) {
|
|
423
|
+
assertDynamicFramework(debug);
|
|
424
|
+
assertDynamicFramework(release);
|
|
425
|
+
}
|
|
426
|
+
return {
|
|
427
|
+
id,
|
|
428
|
+
frameworkName: debug.frameworkName,
|
|
429
|
+
executableName: debug.executableName,
|
|
430
|
+
linkage: 'dynamic',
|
|
431
|
+
slices: debug.slices,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function transformReactModuleMap(contents /*: string */) /*: string */ {
|
|
436
|
+
if (!/framework\s+module\s+React\b/.test(contents)) {
|
|
437
|
+
throw new Error('React module map does not declare framework module React');
|
|
438
|
+
}
|
|
439
|
+
return contents
|
|
440
|
+
.replace(/framework\s+module\s+React\b/, 'module React')
|
|
441
|
+
.replace(
|
|
442
|
+
/(umbrella header|textual header|private header|header)\s+"([^"]+)"/g,
|
|
443
|
+
(match, kind, header) =>
|
|
444
|
+
`${kind} "${header.startsWith('React/') ? header : `React/${header}`}"`,
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function replaceDirectoryAtomically(
|
|
449
|
+
destination /*: string */,
|
|
450
|
+
populate /*: (temp: string) => void */,
|
|
451
|
+
) /*: void */ {
|
|
452
|
+
const temp = `${destination}.tmp-${process.pid}`;
|
|
453
|
+
fs.rmSync(temp, {recursive: true, force: true});
|
|
454
|
+
fs.mkdirSync(temp, {recursive: true});
|
|
455
|
+
populate(temp);
|
|
456
|
+
fs.rmSync(destination, {recursive: true, force: true});
|
|
457
|
+
fs.renameSync(temp, destination);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function stageReactHeadersTarget(
|
|
461
|
+
packageRoot /*: string */,
|
|
462
|
+
canonicalReact /*: ParsedXcframework */,
|
|
463
|
+
) /*: void */ {
|
|
464
|
+
const slice = canonicalReact.slices[0];
|
|
465
|
+
const framework = path.join(
|
|
466
|
+
canonicalReact.path,
|
|
467
|
+
slice.libraryIdentifier,
|
|
468
|
+
slice.libraryPath,
|
|
469
|
+
);
|
|
470
|
+
const sourceHeaders = path.join(framework, 'Headers');
|
|
471
|
+
const sourceModuleMap = path.join(framework, 'Modules', 'module.modulemap');
|
|
472
|
+
if (!fs.existsSync(sourceHeaders) || !fs.existsSync(sourceModuleMap)) {
|
|
473
|
+
throw new Error(
|
|
474
|
+
`React headers or module map missing from canonical framework ${framework}`,
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
const destination = path.join(packageRoot, 'ReactHeadersTarget');
|
|
478
|
+
const transformed = transformReactModuleMap(
|
|
479
|
+
fs.readFileSync(sourceModuleMap, 'utf8'),
|
|
480
|
+
);
|
|
481
|
+
const expectedHash = crypto
|
|
482
|
+
.createHash('sha256')
|
|
483
|
+
.update(hashTrees([sourceHeaders]))
|
|
484
|
+
.update(transformed)
|
|
485
|
+
.digest('hex');
|
|
486
|
+
const marker = path.join(destination, '.headers-hash');
|
|
487
|
+
try {
|
|
488
|
+
if (fs.readFileSync(marker, 'utf8') === `${expectedHash}\n`) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
} catch {}
|
|
492
|
+
replaceDirectoryAtomically(destination, temp => {
|
|
493
|
+
const include = path.join(temp, 'include');
|
|
494
|
+
fs.mkdirSync(include, {recursive: true});
|
|
495
|
+
fs.cpSync(sourceHeaders, path.join(include, 'React'), {
|
|
496
|
+
recursive: true,
|
|
497
|
+
dereference: true,
|
|
498
|
+
});
|
|
499
|
+
fs.writeFileSync(
|
|
500
|
+
path.join(include, 'module.modulemap'),
|
|
501
|
+
transformed,
|
|
502
|
+
'utf8',
|
|
503
|
+
);
|
|
504
|
+
fs.writeFileSync(
|
|
505
|
+
path.join(temp, 'empty.c'),
|
|
506
|
+
'void RNReactHeadersAnchor(void) {}\n',
|
|
507
|
+
'utf8',
|
|
508
|
+
);
|
|
509
|
+
fs.writeFileSync(path.join(temp, '.headers-hash'), `${expectedHash}\n`);
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function ensureSymlink(target /*: string */, link /*: string */) /*: void */ {
|
|
514
|
+
try {
|
|
515
|
+
if (fs.lstatSync(link).isSymbolicLink()) {
|
|
516
|
+
const current = path.resolve(path.dirname(link), fs.readlinkSync(link));
|
|
517
|
+
if (current === path.resolve(target)) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
} catch {}
|
|
522
|
+
const temp = `${link}.tmp-${process.pid}`;
|
|
523
|
+
fs.rmSync(temp, {recursive: true, force: true});
|
|
524
|
+
fs.mkdirSync(path.dirname(link), {recursive: true});
|
|
525
|
+
fs.symlinkSync(target, temp);
|
|
526
|
+
fs.rmSync(link, {recursive: true, force: true});
|
|
527
|
+
fs.renameSync(temp, link);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function symlinkPointsTo(
|
|
531
|
+
target /*: string */,
|
|
532
|
+
link /*: string */,
|
|
533
|
+
) /*: boolean */ {
|
|
534
|
+
try {
|
|
535
|
+
return (
|
|
536
|
+
fs.lstatSync(link).isSymbolicLink() &&
|
|
537
|
+
path.resolve(path.dirname(link), fs.readlinkSync(link)) ===
|
|
538
|
+
path.resolve(target)
|
|
539
|
+
);
|
|
540
|
+
} catch {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function sourceIdentity(framework /*: StagedFramework */) /*: {
|
|
546
|
+
debug: {path: string, info: number, binaries: Array<{path: string, size: number, mtime: number}>},
|
|
547
|
+
release: {path: string, info: number, binaries: Array<{path: string, size: number, mtime: number}>},
|
|
548
|
+
} */ {
|
|
549
|
+
const identity /*: {[string]: {path: string, info: number, binaries: Array<{path: string, size: number, mtime: number}>}} */ =
|
|
550
|
+
{};
|
|
551
|
+
for (const flavor of FLAVORS) {
|
|
552
|
+
const xcframeworkPath = framework.sourcePaths[flavor];
|
|
553
|
+
identity[flavor] = {
|
|
554
|
+
path: fs.realpathSync(xcframeworkPath),
|
|
555
|
+
info: fs.statSync(path.join(xcframeworkPath, 'Info.plist')).mtimeMs,
|
|
556
|
+
binaries: framework.slices.map(slice => {
|
|
557
|
+
const binary = path.join(
|
|
558
|
+
xcframeworkPath,
|
|
559
|
+
slice.libraryIdentifier,
|
|
560
|
+
slice.binaryPath,
|
|
561
|
+
);
|
|
562
|
+
const stat = fs.statSync(binary);
|
|
563
|
+
return {path: slice.binaryPath, size: stat.size, mtime: stat.mtimeMs};
|
|
564
|
+
}),
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
debug: identity.debug,
|
|
569
|
+
release: identity.release,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function publicationIsCurrent(
|
|
574
|
+
packageRoot /*: string */,
|
|
575
|
+
sources /*: ReadonlyArray<StagedFramework> */,
|
|
576
|
+
debugIndex /*: ArtifactIndex */,
|
|
577
|
+
stamp /*: string */,
|
|
578
|
+
manifestContents /*: string */,
|
|
579
|
+
) /*: boolean */ {
|
|
580
|
+
try {
|
|
581
|
+
const pendingStamp = path.join(packageRoot, PENDING_ARTIFACT_STAMP);
|
|
582
|
+
const currentStamp = fs.existsSync(pendingStamp)
|
|
583
|
+
? pendingStamp
|
|
584
|
+
: path.join(packageRoot, '.artifact-stamp');
|
|
585
|
+
if (
|
|
586
|
+
fs.readFileSync(currentStamp, 'utf8') !== stamp ||
|
|
587
|
+
fs.readFileSync(
|
|
588
|
+
path.join(packageRoot, FLAVORED_FRAMEWORKS_MANIFEST),
|
|
589
|
+
'utf8',
|
|
590
|
+
) !== manifestContents ||
|
|
591
|
+
!fs.existsSync(
|
|
592
|
+
path.join(packageRoot, 'ReactHeadersTarget', '.headers-hash'),
|
|
593
|
+
)
|
|
594
|
+
) {
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
for (const flavor of FLAVORS) {
|
|
598
|
+
for (const framework of sources) {
|
|
599
|
+
if (
|
|
600
|
+
!symlinkPointsTo(
|
|
601
|
+
framework.sourcePaths[flavor],
|
|
602
|
+
path.join(packageRoot, flavor, framework.artifactRelativePath),
|
|
603
|
+
)
|
|
604
|
+
) {
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return INVARIANT_BINARY_TARGETS.every(name =>
|
|
610
|
+
symlinkPointsTo(
|
|
611
|
+
debugIndex[name].xcframeworkPath,
|
|
612
|
+
path.join(packageRoot, `${name}.xcframework`),
|
|
613
|
+
),
|
|
614
|
+
);
|
|
615
|
+
} catch {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function publishDirectoryAtomically(
|
|
621
|
+
staged /*: string */,
|
|
622
|
+
destination /*: string */,
|
|
623
|
+
) /*: void */ {
|
|
624
|
+
const previous = `${destination}.previous-${process.pid}`;
|
|
625
|
+
fs.rmSync(previous, {recursive: true, force: true});
|
|
626
|
+
const hadPrevious = fs.existsSync(destination);
|
|
627
|
+
if (hadPrevious) {
|
|
628
|
+
fs.renameSync(destination, previous);
|
|
629
|
+
}
|
|
630
|
+
try {
|
|
631
|
+
fs.renameSync(staged, destination);
|
|
632
|
+
} catch (error) {
|
|
633
|
+
if (hadPrevious && !fs.existsSync(destination)) {
|
|
634
|
+
fs.renameSync(previous, destination);
|
|
635
|
+
}
|
|
636
|
+
throw error;
|
|
637
|
+
}
|
|
638
|
+
fs.rmSync(previous, {recursive: true, force: true});
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function writeIfChanged(filePath /*: string */, contents /*: string */) {
|
|
642
|
+
try {
|
|
643
|
+
if (fs.readFileSync(filePath, 'utf8') === contents) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
} catch {}
|
|
647
|
+
fs.mkdirSync(path.dirname(filePath), {recursive: true});
|
|
648
|
+
fs.writeFileSync(filePath, contents, 'utf8');
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function readPluginFrameworks(
|
|
653
|
+
appRoot /*: string */,
|
|
654
|
+
) /*: Array<PluginFlavoredFramework> */ {
|
|
655
|
+
const manifestPath = path.join(appRoot, PLUGIN_FRAMEWORKS_MANIFEST);
|
|
656
|
+
if (!fs.existsSync(manifestPath)) {
|
|
657
|
+
return [];
|
|
658
|
+
}
|
|
659
|
+
const parsed = readJson(manifestPath);
|
|
660
|
+
if (!Array.isArray(parsed)) {
|
|
661
|
+
throw new Error(`${PLUGIN_FRAMEWORKS_MANIFEST} must contain an array`);
|
|
662
|
+
}
|
|
663
|
+
const frameworks /*: Array<PluginFlavoredFramework> */ = [];
|
|
664
|
+
for (const plugin of parsed) {
|
|
665
|
+
if (
|
|
666
|
+
plugin == null ||
|
|
667
|
+
typeof plugin !== 'object' ||
|
|
668
|
+
typeof plugin.id !== 'string' ||
|
|
669
|
+
!/^[A-Za-z0-9_.-]+$/.test(plugin.id) ||
|
|
670
|
+
typeof plugin.frameworkName !== 'string' ||
|
|
671
|
+
plugin.linkage !== 'dynamic' ||
|
|
672
|
+
plugin.flavors == null ||
|
|
673
|
+
typeof plugin.flavors !== 'object' ||
|
|
674
|
+
typeof plugin.flavors.debug !== 'string' ||
|
|
675
|
+
typeof plugin.flavors.release !== 'string'
|
|
676
|
+
) {
|
|
677
|
+
throw new Error(
|
|
678
|
+
'plugin flavoredFrameworks entries require a stable id, frameworkName, ' +
|
|
679
|
+
'linkage="dynamic", and absolute debug/release paths',
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
frameworks.push({
|
|
683
|
+
id: plugin.id,
|
|
684
|
+
frameworkName: plugin.frameworkName,
|
|
685
|
+
linkage: 'dynamic',
|
|
686
|
+
flavors: {
|
|
687
|
+
debug: plugin.flavors.debug,
|
|
688
|
+
release: plugin.flavors.release,
|
|
689
|
+
},
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
return frameworks;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function prepareFlavoredFrameworks(
|
|
696
|
+
opts /*: {
|
|
697
|
+
appRoot: string,
|
|
698
|
+
debugArtifactsDir: string,
|
|
699
|
+
releaseArtifactsDir: string,
|
|
700
|
+
validateBinaries?: boolean,
|
|
701
|
+
} */,
|
|
702
|
+
) /*: FlavoredFrameworksManifest */ {
|
|
703
|
+
const appRoot = path.resolve(opts.appRoot);
|
|
704
|
+
const packageRoot = path.join(appRoot, 'build', 'xcframeworks');
|
|
705
|
+
const debugIndex = readArtifactIndex(path.resolve(opts.debugArtifactsDir));
|
|
706
|
+
const releaseIndex = readArtifactIndex(
|
|
707
|
+
path.resolve(opts.releaseArtifactsDir),
|
|
708
|
+
);
|
|
709
|
+
const validateBinaries = opts.validateBinaries !== false;
|
|
710
|
+
const sources /*: Array<StagedFramework> */ = [];
|
|
711
|
+
const destinations /*: Set<string> */ = new Set();
|
|
712
|
+
const frameworkIds /*: Set<string> */ = new Set();
|
|
713
|
+
|
|
714
|
+
for (const builtin of BUILTIN_FRAMEWORKS) {
|
|
715
|
+
const debugEntry = debugIndex[builtin.artifactName];
|
|
716
|
+
const releaseEntry = releaseIndex[builtin.artifactName];
|
|
717
|
+
if (debugEntry == null || releaseEntry == null) {
|
|
718
|
+
throw new Error(
|
|
719
|
+
`both flavors must contain '${builtin.artifactName}' in artifacts.json`,
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
const normalized = validatePair(
|
|
723
|
+
builtin.id,
|
|
724
|
+
path.resolve(debugEntry.xcframeworkPath),
|
|
725
|
+
path.resolve(releaseEntry.xcframeworkPath),
|
|
726
|
+
null,
|
|
727
|
+
validateBinaries,
|
|
728
|
+
);
|
|
729
|
+
const artifactRelativePath = `${builtin.artifactName}.xcframework`;
|
|
730
|
+
if (destinations.has(normalized.frameworkName)) {
|
|
731
|
+
throw new Error(
|
|
732
|
+
`multiple flavored frameworks embed as ${normalized.frameworkName}.framework`,
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
frameworkIds.add(normalized.id);
|
|
736
|
+
destinations.add(normalized.frameworkName);
|
|
737
|
+
sources.push({
|
|
738
|
+
id: normalized.id,
|
|
739
|
+
frameworkName: normalized.frameworkName,
|
|
740
|
+
executableName: normalized.executableName,
|
|
741
|
+
linkage: 'dynamic',
|
|
742
|
+
slices: normalized.slices,
|
|
743
|
+
artifactRelativePath,
|
|
744
|
+
sourcePaths: {
|
|
745
|
+
debug: path.resolve(debugEntry.xcframeworkPath),
|
|
746
|
+
release: path.resolve(releaseEntry.xcframeworkPath),
|
|
747
|
+
},
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
for (const plugin of readPluginFrameworks(appRoot)) {
|
|
752
|
+
if (
|
|
753
|
+
plugin == null ||
|
|
754
|
+
typeof plugin !== 'object' ||
|
|
755
|
+
typeof plugin.id !== 'string' ||
|
|
756
|
+
!/^[A-Za-z0-9_.-]+$/.test(plugin.id) ||
|
|
757
|
+
typeof plugin.frameworkName !== 'string' ||
|
|
758
|
+
plugin.linkage !== 'dynamic' ||
|
|
759
|
+
plugin.flavors == null ||
|
|
760
|
+
typeof plugin.flavors.debug !== 'string' ||
|
|
761
|
+
typeof plugin.flavors.release !== 'string' ||
|
|
762
|
+
!path.isAbsolute(plugin.flavors.debug) ||
|
|
763
|
+
!path.isAbsolute(plugin.flavors.release)
|
|
764
|
+
) {
|
|
765
|
+
throw new Error(
|
|
766
|
+
'plugin flavoredFrameworks entries require a stable id, frameworkName, ' +
|
|
767
|
+
'linkage="dynamic", and absolute debug/release paths',
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
const normalized = validatePair(
|
|
771
|
+
plugin.id,
|
|
772
|
+
plugin.flavors.debug,
|
|
773
|
+
plugin.flavors.release,
|
|
774
|
+
plugin.frameworkName,
|
|
775
|
+
validateBinaries,
|
|
776
|
+
);
|
|
777
|
+
if (frameworkIds.has(normalized.id)) {
|
|
778
|
+
throw new Error(`duplicate flavored framework id '${normalized.id}'`);
|
|
779
|
+
}
|
|
780
|
+
if (destinations.has(normalized.frameworkName)) {
|
|
781
|
+
throw new Error(
|
|
782
|
+
`multiple flavored frameworks embed as ${normalized.frameworkName}.framework`,
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
frameworkIds.add(normalized.id);
|
|
786
|
+
destinations.add(normalized.frameworkName);
|
|
787
|
+
sources.push({
|
|
788
|
+
id: normalized.id,
|
|
789
|
+
frameworkName: normalized.frameworkName,
|
|
790
|
+
executableName: normalized.executableName,
|
|
791
|
+
linkage: 'dynamic',
|
|
792
|
+
slices: normalized.slices,
|
|
793
|
+
artifactRelativePath: `plugins/${plugin.id}.xcframework`,
|
|
794
|
+
sourcePaths: {
|
|
795
|
+
debug: plugin.flavors.debug,
|
|
796
|
+
release: plugin.flavors.release,
|
|
797
|
+
},
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
for (const name of INVARIANT_BINARY_TARGETS) {
|
|
802
|
+
const debugEntry = debugIndex[name];
|
|
803
|
+
const releaseEntry = releaseIndex[name];
|
|
804
|
+
if (debugEntry == null || releaseEntry == null) {
|
|
805
|
+
throw new Error(`both flavors must contain invariant target '${name}'`);
|
|
806
|
+
}
|
|
807
|
+
const debugHash = assertOneHash(
|
|
808
|
+
`${name} Debug`,
|
|
809
|
+
invariantHeadersHashes(debugEntry.xcframeworkPath),
|
|
810
|
+
);
|
|
811
|
+
const releaseHash = assertOneHash(
|
|
812
|
+
`${name} Release`,
|
|
813
|
+
invariantHeadersHashes(releaseEntry.xcframeworkPath),
|
|
814
|
+
);
|
|
815
|
+
if (debugHash !== releaseHash) {
|
|
816
|
+
throw new Error(`${name} headers differ between Debug and Release`);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const canonicalReact = parseXcframework(debugIndex.React.xcframeworkPath);
|
|
821
|
+
const manifest /*: FlavoredFrameworksManifest */ = {
|
|
822
|
+
version: 1,
|
|
823
|
+
frameworks: sources.map(({sourcePaths, ...framework}) => framework),
|
|
824
|
+
};
|
|
825
|
+
const contents = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
826
|
+
const stampMaterial = JSON.stringify(
|
|
827
|
+
sources.map(framework => ({
|
|
828
|
+
id: framework.id,
|
|
829
|
+
sourceIdentity: sourceIdentity(framework),
|
|
830
|
+
})),
|
|
831
|
+
);
|
|
832
|
+
const stamp = `${crypto
|
|
833
|
+
.createHash('sha256')
|
|
834
|
+
.update(contents)
|
|
835
|
+
.update(stampMaterial)
|
|
836
|
+
.digest('hex')}\n`;
|
|
837
|
+
if (publicationIsCurrent(packageRoot, sources, debugIndex, stamp, contents)) {
|
|
838
|
+
return manifest;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
const staged = `${packageRoot}.publish-${process.pid}`;
|
|
842
|
+
fs.rmSync(staged, {recursive: true, force: true});
|
|
843
|
+
fs.mkdirSync(staged, {recursive: true});
|
|
844
|
+
try {
|
|
845
|
+
for (const flavor of FLAVORS) {
|
|
846
|
+
for (const framework of sources) {
|
|
847
|
+
ensureSymlink(
|
|
848
|
+
framework.sourcePaths[flavor],
|
|
849
|
+
path.join(staged, flavor, framework.artifactRelativePath),
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
for (const name of INVARIANT_BINARY_TARGETS) {
|
|
854
|
+
ensureSymlink(
|
|
855
|
+
debugIndex[name].xcframeworkPath,
|
|
856
|
+
path.join(staged, `${name}.xcframework`),
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
stageReactHeadersTarget(staged, canonicalReact);
|
|
860
|
+
writeIfChanged(path.join(staged, FLAVORED_FRAMEWORKS_MANIFEST), contents);
|
|
861
|
+
writeIfChanged(path.join(staged, PENDING_ARTIFACT_STAMP), stamp);
|
|
862
|
+
const existingPackage = path.join(packageRoot, 'Package.swift');
|
|
863
|
+
if (fs.existsSync(existingPackage)) {
|
|
864
|
+
fs.copyFileSync(existingPackage, path.join(staged, 'Package.swift'));
|
|
865
|
+
}
|
|
866
|
+
const existingStamp = path.join(packageRoot, '.artifact-stamp');
|
|
867
|
+
if (fs.existsSync(existingStamp)) {
|
|
868
|
+
fs.copyFileSync(existingStamp, path.join(staged, '.artifact-stamp'));
|
|
869
|
+
}
|
|
870
|
+
fs.mkdirSync(path.dirname(packageRoot), {recursive: true});
|
|
871
|
+
publishDirectoryAtomically(staged, packageRoot);
|
|
872
|
+
} finally {
|
|
873
|
+
fs.rmSync(staged, {recursive: true, force: true});
|
|
874
|
+
}
|
|
875
|
+
return manifest;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
function finalizeArtifactPublication(appRoot /*: string */) /*: boolean */ {
|
|
879
|
+
const packageRoot = path.join(path.resolve(appRoot), 'build', 'xcframeworks');
|
|
880
|
+
const pending = path.join(packageRoot, PENDING_ARTIFACT_STAMP);
|
|
881
|
+
if (!fs.existsSync(pending)) {
|
|
882
|
+
return false;
|
|
883
|
+
}
|
|
884
|
+
const stamp = fs.readFileSync(pending, 'utf8');
|
|
885
|
+
writeIfChanged(path.join(packageRoot, '.artifact-stamp'), stamp);
|
|
886
|
+
fs.rmSync(pending, {force: true});
|
|
887
|
+
return true;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
function readFlavoredFrameworksManifest(
|
|
891
|
+
appRoot /*: string */,
|
|
892
|
+
) /*: FlavoredFrameworksManifest */ {
|
|
893
|
+
const manifestPath = path.join(
|
|
894
|
+
appRoot,
|
|
895
|
+
'build',
|
|
896
|
+
'xcframeworks',
|
|
897
|
+
FLAVORED_FRAMEWORKS_MANIFEST,
|
|
898
|
+
);
|
|
899
|
+
if (!fs.existsSync(manifestPath)) {
|
|
900
|
+
throw new Error(
|
|
901
|
+
`${FLAVORED_FRAMEWORKS_MANIFEST} is missing; run npx react-native spm update`,
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
const parsed = readJson(manifestPath);
|
|
905
|
+
if (
|
|
906
|
+
parsed == null ||
|
|
907
|
+
typeof parsed !== 'object' ||
|
|
908
|
+
parsed.version !== 1 ||
|
|
909
|
+
!Array.isArray(parsed.frameworks)
|
|
910
|
+
) {
|
|
911
|
+
throw new Error(`${FLAVORED_FRAMEWORKS_MANIFEST} has an invalid format`);
|
|
912
|
+
}
|
|
913
|
+
const frameworks /*: Array<FlavoredFrameworkManifestEntry> */ = [];
|
|
914
|
+
for (const framework of parsed.frameworks) {
|
|
915
|
+
if (
|
|
916
|
+
framework == null ||
|
|
917
|
+
typeof framework !== 'object' ||
|
|
918
|
+
typeof framework.id !== 'string' ||
|
|
919
|
+
typeof framework.frameworkName !== 'string' ||
|
|
920
|
+
typeof framework.executableName !== 'string' ||
|
|
921
|
+
framework.linkage !== 'dynamic' ||
|
|
922
|
+
typeof framework.artifactRelativePath !== 'string' ||
|
|
923
|
+
!Array.isArray(framework.slices)
|
|
924
|
+
) {
|
|
925
|
+
throw new Error(`${FLAVORED_FRAMEWORKS_MANIFEST} has an invalid entry`);
|
|
926
|
+
}
|
|
927
|
+
const id = framework.id;
|
|
928
|
+
const frameworkName = framework.frameworkName;
|
|
929
|
+
const executableName = framework.executableName;
|
|
930
|
+
const artifactRelativePath = framework.artifactRelativePath;
|
|
931
|
+
const rawSlices = framework.slices;
|
|
932
|
+
const slices /*: Array<XcframeworkSlice> */ = [];
|
|
933
|
+
for (const slice of rawSlices) {
|
|
934
|
+
if (
|
|
935
|
+
slice == null ||
|
|
936
|
+
typeof slice !== 'object' ||
|
|
937
|
+
typeof slice.sdk !== 'string' ||
|
|
938
|
+
typeof slice.platform !== 'string' ||
|
|
939
|
+
(slice.variant != null && typeof slice.variant !== 'string') ||
|
|
940
|
+
!Array.isArray(slice.architectures) ||
|
|
941
|
+
typeof slice.libraryIdentifier !== 'string' ||
|
|
942
|
+
typeof slice.libraryPath !== 'string' ||
|
|
943
|
+
typeof slice.binaryPath !== 'string'
|
|
944
|
+
) {
|
|
945
|
+
throw new Error(
|
|
946
|
+
`${FLAVORED_FRAMEWORKS_MANIFEST} has an invalid XCFramework slice`,
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
const architectures /*: Array<string> */ = [];
|
|
950
|
+
for (const architecture of slice.architectures) {
|
|
951
|
+
if (typeof architecture !== 'string') {
|
|
952
|
+
throw new Error(
|
|
953
|
+
`${FLAVORED_FRAMEWORKS_MANIFEST} has an invalid XCFramework architecture`,
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
architectures.push(architecture);
|
|
957
|
+
}
|
|
958
|
+
const sdk = requireString(slice.sdk, 'XCFramework SDK');
|
|
959
|
+
const platform = requireString(slice.platform, 'XCFramework platform');
|
|
960
|
+
const variant = typeof slice.variant === 'string' ? slice.variant : null;
|
|
961
|
+
const libraryIdentifier = requireString(
|
|
962
|
+
slice.libraryIdentifier,
|
|
963
|
+
'XCFramework library identifier',
|
|
964
|
+
);
|
|
965
|
+
const libraryPath = requireString(
|
|
966
|
+
slice.libraryPath,
|
|
967
|
+
'XCFramework library path',
|
|
968
|
+
);
|
|
969
|
+
const binaryPath = requireString(
|
|
970
|
+
slice.binaryPath,
|
|
971
|
+
'XCFramework binary path',
|
|
972
|
+
);
|
|
973
|
+
slices.push({
|
|
974
|
+
sdk,
|
|
975
|
+
platform,
|
|
976
|
+
variant,
|
|
977
|
+
architectures,
|
|
978
|
+
libraryIdentifier,
|
|
979
|
+
libraryPath,
|
|
980
|
+
binaryPath,
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
frameworks.push({
|
|
984
|
+
id,
|
|
985
|
+
frameworkName,
|
|
986
|
+
executableName,
|
|
987
|
+
linkage: 'dynamic',
|
|
988
|
+
artifactRelativePath,
|
|
989
|
+
slices,
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
return {version: 1, frameworks};
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
module.exports = {
|
|
996
|
+
BUILTIN_FRAMEWORKS,
|
|
997
|
+
FLAVORED_FRAMEWORKS_MANIFEST,
|
|
998
|
+
PENDING_ARTIFACT_STAMP,
|
|
999
|
+
INVARIANT_BINARY_TARGETS,
|
|
1000
|
+
PLUGIN_FRAMEWORKS_MANIFEST,
|
|
1001
|
+
parseXcframework,
|
|
1002
|
+
finalizeArtifactPublication,
|
|
1003
|
+
prepareFlavoredFrameworks,
|
|
1004
|
+
readFlavoredFrameworksManifest,
|
|
1005
|
+
sdkConditionForSlice,
|
|
1006
|
+
transformReactModuleMap,
|
|
1007
|
+
validatePair,
|
|
1008
|
+
};
|