@depup/react-native 0.87.0-depup.0 → 0.87.1-depup.0
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/Animated/AnimatedEvent.js +1 -1
- package/Libraries/Animated/AnimatedImplementation.js +6 -2
- package/Libraries/Animated/nodes/AnimatedNode.js +1 -1
- package/Libraries/Animated/nodes/AnimatedValue.js +2 -2
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/README.md +3 -3
- package/React/Base/RCTVersion.m +1 -1
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/modules/intent/IntentModule.kt +7 -3
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
- package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
- package/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontUtils.mm +1 -1
- package/changes.json +2 -2
- package/package.json +12 -12
- package/scripts/cocoapods/rndependencies.rb +31 -25
- package/scripts/replace-rncore-version.js +18 -2
- package/scripts/setup-apple-spm.js +28 -12
- package/scripts/spm/autolinking-plugins.js +80 -0
- package/scripts/spm/download-spm-artifacts.js +64 -6
- package/scripts/spm/expand-spm-dependencies.js +249 -30
- package/scripts/spm/generate-spm-autolinking.js +157 -40
- package/scripts/spm/generate-spm-package.js +30 -18
- package/scripts/spm/generate-spm-xcodeproj.js +595 -71
- package/scripts/spm/scaffold-package-swift.js +65 -24
- package/scripts/spm/spm-pbxproj.js +158 -28
- package/scripts/spm/spm-types.js +29 -3
- package/scripts/spm/spm-utils.js +117 -2
- package/sdks/.hermesv1version +1 -1
- package/sdks/hermes-engine/version.properties +1 -1
- package/types_generated/Libraries/Animated/AnimatedImplementation.d.ts +2 -2
- package/types_generated/Libraries/Animated/nodes/AnimatedNode.d.ts +4 -1
- package/types_generated/Libraries/Animated/nodes/AnimatedValue.d.ts +3 -3
- package/scripts/spm/__doc__/rfc-spm-xcframework.md +0 -707
- package/scripts/spm/__doc__/spm-autolinking-plugins.md +0 -244
- package/scripts/spm/__doc__/spm-header-paths-contract.md +0 -97
- package/scripts/spm/__doc__/spm-plugins-assessment.md +0 -128
- package/scripts/spm/__doc__/spm-scripts.md +0 -486
|
@@ -199,7 +199,7 @@ export class AnimatedEvent {
|
|
|
199
199
|
this._attachedEvent && this._attachedEvent.detach();
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
__getHandler():
|
|
202
|
+
__getHandler(): (...args: any) => void {
|
|
203
203
|
if (this.__isNative) {
|
|
204
204
|
if (__DEV__) {
|
|
205
205
|
let validatedMapping = false;
|
|
@@ -576,10 +576,14 @@ function unforkEventImpl(
|
|
|
576
576
|
}
|
|
577
577
|
}
|
|
578
578
|
|
|
579
|
-
|
|
579
|
+
// NOTE: With `useNativeDriver: true` this returns an `AnimatedEvent` instance
|
|
580
|
+
// rather than a callable handler. That object is only ever meant to be handed
|
|
581
|
+
// straight back to an animated component's event prop, so the declared type
|
|
582
|
+
// describes the handler shape both branches are consumed as.
|
|
583
|
+
const eventImpl: <T>(
|
|
580
584
|
argMapping: ReadonlyArray<?Mapping>,
|
|
581
585
|
config: EventConfig<T>,
|
|
582
|
-
): any {
|
|
586
|
+
) => (...args: Array<any>) => void = function (argMapping, config): any {
|
|
583
587
|
const animatedEvent = new AnimatedEvent(argMapping, config);
|
|
584
588
|
if (animatedEvent.__isNative) {
|
|
585
589
|
return animatedEvent;
|
|
@@ -13,7 +13,7 @@ import type {PlatformConfig} from '../AnimatedPlatformConfig';
|
|
|
13
13
|
import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
|
|
14
14
|
import invariant from 'invariant';
|
|
15
15
|
|
|
16
|
-
type ValueListenerCallback = (state: {value: number
|
|
16
|
+
export type ValueListenerCallback = (state: {value: number}) => unknown;
|
|
17
17
|
|
|
18
18
|
export type AnimatedNodeConfig = Readonly<{
|
|
19
19
|
debugID?: string,
|
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
InterpolationConfigType,
|
|
18
18
|
} from './AnimatedInterpolation';
|
|
19
19
|
import type AnimatedNode from './AnimatedNode';
|
|
20
|
-
import type {AnimatedNodeConfig} from './AnimatedNode';
|
|
20
|
+
import type {AnimatedNodeConfig, ValueListenerCallback} from './AnimatedNode';
|
|
21
21
|
import type AnimatedTracking from './AnimatedTracking';
|
|
22
22
|
|
|
23
23
|
import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
|
|
@@ -138,7 +138,7 @@ export default class AnimatedValue extends AnimatedWithChildren {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
addListener(callback:
|
|
141
|
+
addListener(callback: ValueListenerCallback): string {
|
|
142
142
|
const id = super.addListener(callback);
|
|
143
143
|
this._listenerCount++;
|
|
144
144
|
if (this.__isNative) {
|
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ npm install @depup/react-native
|
|
|
13
13
|
|
|
14
14
|
| Field | Value |
|
|
15
15
|
|-------|-------|
|
|
16
|
-
| Original | [react-native](https://www.npmjs.com/package/react-native) @ 0.87.
|
|
17
|
-
| Processed | 2026-08-
|
|
16
|
+
| Original | [react-native](https://www.npmjs.com/package/react-native) @ 0.87.1 |
|
|
17
|
+
| Processed | 2026-08-30 |
|
|
18
18
|
| Smoke test | failed |
|
|
19
19
|
| Deps updated | 15 |
|
|
20
20
|
|
|
@@ -27,7 +27,7 @@ npm install @depup/react-native
|
|
|
27
27
|
| babel-plugin-syntax-hermes-parser | 0.36.1 | ^0.37.0 |
|
|
28
28
|
| commander | ^12.0.0 | ^15.0.0 |
|
|
29
29
|
| memoize-one | ^5.0.0 | ^6.0.0 |
|
|
30
|
-
| pretty-format | ^29.7.0 | ^30.
|
|
30
|
+
| pretty-format | ^29.7.0 | ^30.5.0 |
|
|
31
31
|
| react-devtools-core | ^6.1.5 | ^7.0.1 |
|
|
32
32
|
| react-refresh | ^0.14.0 | ^0.18.0 |
|
|
33
33
|
| regenerator-runtime | ^0.13.2 | ^0.14.1 |
|
package/React/Base/RCTVersion.m
CHANGED
|
@@ -87,11 +87,15 @@ public open class IntentModule(reactContext: ReactApplicationContext) :
|
|
|
87
87
|
override fun onHostResume() {
|
|
88
88
|
reactApplicationContext.removeLifecycleEventListener(this)
|
|
89
89
|
synchronized(this@IntentModule) {
|
|
90
|
-
|
|
90
|
+
// getInitialURL can re-enter and re-add to pendingOpenURLPromises when the activity
|
|
91
|
+
// is still null at resume, so drain a snapshot (after clearing the list and listener)
|
|
92
|
+
// to avoid mutating the list being iterated (ConcurrentModificationException).
|
|
93
|
+
val pendingPromises = ArrayList(pendingOpenURLPromises)
|
|
94
|
+
pendingOpenURLPromises.clear()
|
|
95
|
+
initialURLListener = null
|
|
96
|
+
for (pendingPromise in pendingPromises) {
|
|
91
97
|
getInitialURL(pendingPromise)
|
|
92
98
|
}
|
|
93
|
-
initialURLListener = null
|
|
94
|
-
pendingOpenURLPromises.clear()
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
|
|
15
15
|
#define REACT_NATIVE_VERSION_MAJOR 0
|
|
16
16
|
#define REACT_NATIVE_VERSION_MINOR 87
|
|
17
|
-
#define REACT_NATIVE_VERSION_PATCH
|
|
17
|
+
#define REACT_NATIVE_VERSION_PATCH 1
|
|
18
18
|
|
|
19
19
|
namespace facebook::react {
|
|
20
20
|
|
|
21
21
|
struct ReactNativeVersionType {
|
|
22
22
|
int32_t Major = 0;
|
|
23
23
|
int32_t Minor = 87;
|
|
24
|
-
int32_t Patch =
|
|
24
|
+
int32_t Patch = 1;
|
|
25
25
|
std::string_view Prerelease = "";
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -361,7 +361,7 @@ UIFont *RCTFontWithFontProperties(RCTFontProperties fontProperties)
|
|
|
361
361
|
font = [UIFont fontWithName:fontProperties.family size:effectiveFontSize];
|
|
362
362
|
if (font != nullptr) {
|
|
363
363
|
fontNames = [UIFont fontNamesForFamilyName:font.familyName];
|
|
364
|
-
fontWeight = (fontWeight != 0.0)
|
|
364
|
+
fontWeight = (fontWeight != 0.0) ? fontWeight : RCTGetFontWeight(font);
|
|
365
365
|
} else {
|
|
366
366
|
// Failback to system font.
|
|
367
367
|
font = RCTDefaultFontWithFontProperties(fontProperties);
|
package/changes.json
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"pretty-format": {
|
|
24
24
|
"from": "^29.7.0",
|
|
25
|
-
"to": "^30.
|
|
25
|
+
"to": "^30.5.0"
|
|
26
26
|
},
|
|
27
27
|
"react-devtools-core": {
|
|
28
28
|
"from": "^6.1.5",
|
|
@@ -61,6 +61,6 @@
|
|
|
61
61
|
"to": "^18.1.0"
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
|
-
"timestamp": "2026-08-
|
|
64
|
+
"timestamp": "2026-08-30T00:58:15.370Z",
|
|
65
65
|
"totalUpdated": 15
|
|
66
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@depup/react-native",
|
|
3
|
-
"version": "0.87.
|
|
3
|
+
"version": "0.87.1-depup.0",
|
|
4
4
|
"description": "A framework for building native apps using React (with updated dependencies)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -155,25 +155,25 @@
|
|
|
155
155
|
}
|
|
156
156
|
},
|
|
157
157
|
"dependencies": {
|
|
158
|
-
"@react-native/asset-utils": "0.87.
|
|
159
|
-
"@react-native/codegen": "0.87.
|
|
160
|
-
"@react-native/community-cli-plugin": "0.87.
|
|
161
|
-
"@react-native/gradle-plugin": "0.87.
|
|
162
|
-
"@react-native/normalize-colors": "0.87.
|
|
163
|
-
"@react-native/virtualized-lists": "0.87.
|
|
158
|
+
"@react-native/asset-utils": "0.87.1",
|
|
159
|
+
"@react-native/codegen": "0.87.1",
|
|
160
|
+
"@react-native/community-cli-plugin": "0.87.1",
|
|
161
|
+
"@react-native/gradle-plugin": "0.87.1",
|
|
162
|
+
"@react-native/normalize-colors": "0.87.1",
|
|
163
|
+
"@react-native/virtualized-lists": "0.87.1",
|
|
164
164
|
"anser": "^2.3.5",
|
|
165
165
|
"ansi-regex": "^6.3.0",
|
|
166
166
|
"babel-plugin-syntax-hermes-parser": "^0.37.0",
|
|
167
167
|
"base64-js": "^1.5.1",
|
|
168
168
|
"commander": "^15.0.0",
|
|
169
169
|
"flow-enums-runtime": "^0.0.6",
|
|
170
|
-
"hermes-compiler": "250829098.0.
|
|
170
|
+
"hermes-compiler": "250829098.0.17",
|
|
171
171
|
"invariant": "^2.2.4",
|
|
172
172
|
"memoize-one": "^6.0.0",
|
|
173
173
|
"metro-runtime": "^0.87.0",
|
|
174
174
|
"metro-source-map": "^0.87.0",
|
|
175
175
|
"nullthrows": "^1.1.1",
|
|
176
|
-
"pretty-format": "^30.
|
|
176
|
+
"pretty-format": "^30.5.0",
|
|
177
177
|
"promise": "^8.3.0",
|
|
178
178
|
"react-devtools-core": "^7.0.1",
|
|
179
179
|
"react-refresh": "^0.18.0",
|
|
@@ -245,7 +245,7 @@
|
|
|
245
245
|
},
|
|
246
246
|
"pretty-format": {
|
|
247
247
|
"from": "^29.7.0",
|
|
248
|
-
"to": "^30.
|
|
248
|
+
"to": "^30.5.0"
|
|
249
249
|
},
|
|
250
250
|
"react-devtools-core": {
|
|
251
251
|
"from": "^6.1.5",
|
|
@@ -286,8 +286,8 @@
|
|
|
286
286
|
},
|
|
287
287
|
"depsUpdated": 15,
|
|
288
288
|
"originalPackage": "react-native",
|
|
289
|
-
"originalVersion": "0.87.
|
|
290
|
-
"processedAt": "2026-08-
|
|
289
|
+
"originalVersion": "0.87.1",
|
|
290
|
+
"processedAt": "2026-08-30T00:58:26.000Z",
|
|
291
291
|
"smokeTest": "failed"
|
|
292
292
|
}
|
|
293
293
|
}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
require "json"
|
|
7
7
|
require 'net/http'
|
|
8
8
|
require 'rexml/document'
|
|
9
|
-
require 'shellwords'
|
|
10
9
|
|
|
11
10
|
require_relative './utils.rb'
|
|
12
11
|
|
|
@@ -34,38 +33,26 @@ def add_rn_third_party_dependencies(s)
|
|
|
34
33
|
s.dependency "RCT-Folly/Fabric"
|
|
35
34
|
end
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
header_search_paths << "$(PODS_ROOT)/fast_float/include"
|
|
47
|
-
header_search_paths << "$(PODS_ROOT)/fmt/include"
|
|
48
|
-
header_search_paths << "$(PODS_ROOT)/SocketRocket"
|
|
49
|
-
header_search_paths << "$(PODS_ROOT)/RCT-Folly"
|
|
50
|
-
|
|
51
|
-
# uniq so a second call on the same spec can't duplicate entries.
|
|
52
|
-
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths.uniq
|
|
36
|
+
ReactNativeDependenciesUtils.append_header_search_paths(current_pod_target_xcconfig, [
|
|
37
|
+
"$(PODS_ROOT)/glog",
|
|
38
|
+
"$(PODS_ROOT)/boost",
|
|
39
|
+
"$(PODS_ROOT)/DoubleConversion",
|
|
40
|
+
"$(PODS_ROOT)/fast_float/include",
|
|
41
|
+
"$(PODS_ROOT)/fmt/include",
|
|
42
|
+
"$(PODS_ROOT)/SocketRocket",
|
|
43
|
+
"$(PODS_ROOT)/RCT-Folly",
|
|
44
|
+
])
|
|
53
45
|
else
|
|
54
46
|
# Prebuilt-deps mode: this pod SELF-SERVES the third-party headers from its
|
|
55
47
|
# own xcframework (incl. SocketRocket - sole supplier in this mode). See
|
|
56
48
|
# scripts/cocoapods/__docs__/prebuilt-deps.md for the full contract.
|
|
57
49
|
s.dependency "ReactNativeDependencies"
|
|
58
50
|
|
|
59
|
-
header_search_paths = current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] || []
|
|
60
|
-
if header_search_paths.is_a?(String)
|
|
61
|
-
header_search_paths = Shellwords.shellsplit(header_search_paths)
|
|
62
|
-
end
|
|
63
51
|
# Artifact headers are flattened into the pod-local Headers/ by the podspec
|
|
64
52
|
# prepare_command (see __docs__/prebuilt-deps.md).
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
current_pod_target_xcconfig["HEADER_SEARCH_PATHS"] = header_search_paths.uniq
|
|
53
|
+
ReactNativeDependenciesUtils.append_header_search_paths(current_pod_target_xcconfig, [
|
|
54
|
+
"$(PODS_ROOT)/ReactNativeDependencies/Headers",
|
|
55
|
+
])
|
|
69
56
|
end
|
|
70
57
|
|
|
71
58
|
s.pod_target_xcconfig = current_pod_target_xcconfig
|
|
@@ -148,6 +135,25 @@ class ReactNativeDependenciesUtils
|
|
|
148
135
|
end
|
|
149
136
|
end
|
|
150
137
|
|
|
138
|
+
# Xcode splits HEADER_SEARCH_PATHS on whitespace, so every path we add is
|
|
139
|
+
# quoted - PODS_ROOT can expand to a directory with spaces in its name.
|
|
140
|
+
# Paths already in the xcconfig are left untouched: they carry the podspec
|
|
141
|
+
# author's own quoting, and re-quoting them would break it.
|
|
142
|
+
def self.append_header_search_paths(xcconfig, paths)
|
|
143
|
+
quoted = paths.map { |path| "\"#{path}\"" }
|
|
144
|
+
existing = xcconfig["HEADER_SEARCH_PATHS"]
|
|
145
|
+
|
|
146
|
+
# reject so a second call on the same spec can't duplicate entries.
|
|
147
|
+
case existing
|
|
148
|
+
when nil
|
|
149
|
+
xcconfig["HEADER_SEARCH_PATHS"] = quoted
|
|
150
|
+
when Array
|
|
151
|
+
xcconfig["HEADER_SEARCH_PATHS"] = existing + quoted.reject { |path| existing.include?(path) }
|
|
152
|
+
else
|
|
153
|
+
xcconfig["HEADER_SEARCH_PATHS"] = ([existing] + quoted.reject { |path| existing.include?(path) }).join(" ")
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
151
157
|
def self.abort_if_use_local_rndeps_with_no_file()
|
|
152
158
|
if !File.exist?(ENV["RCT_USE_LOCAL_RN_DEP"])
|
|
153
159
|
abort("RCT_USE_LOCAL_RN_DEP is set to #{ENV["RCT_USE_LOCAL_RN_DEP"]} but the file does not exist!")
|
|
@@ -18,6 +18,9 @@ const yargs = require('yargs');
|
|
|
18
18
|
|
|
19
19
|
const LAST_BUILD_FILENAME = 'React-Core-prebuilt/.last_build_configuration';
|
|
20
20
|
|
|
21
|
+
// Not a valid configuration, so finding it means the swap did not finish.
|
|
22
|
+
const REPLACEMENT_IN_PROGRESS = 'in-progress';
|
|
23
|
+
|
|
21
24
|
function validateBuildConfiguration(configuration /*: string */) {
|
|
22
25
|
if (!['Debug', 'Release'].includes(configuration)) {
|
|
23
26
|
throw new Error(`Invalid configuration ${configuration}`);
|
|
@@ -42,10 +45,12 @@ function shouldReplaceRnCoreConfiguration(configuration /*: string */) {
|
|
|
42
45
|
);
|
|
43
46
|
return false;
|
|
44
47
|
}
|
|
48
|
+
return true;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
|
-
//
|
|
48
|
-
|
|
51
|
+
// With no marker the on-disk flavor is Debug: the podspec installs the debug
|
|
52
|
+
// tarball (see resolve_podspec_source in scripts/cocoapods/rncore.rb).
|
|
53
|
+
if (configuration === 'Debug') {
|
|
49
54
|
console.log(
|
|
50
55
|
'No previous build detected, but Debug Configuration. No need to replace React-Core-prebuilt',
|
|
51
56
|
);
|
|
@@ -130,6 +135,10 @@ function updateLastBuildConfiguration(configuration /*: string */) {
|
|
|
130
135
|
fs.writeFileSync(LAST_BUILD_FILENAME, configuration);
|
|
131
136
|
}
|
|
132
137
|
|
|
138
|
+
function markReplacementInProgress() /*: void */ {
|
|
139
|
+
fs.writeFileSync(LAST_BUILD_FILENAME, REPLACEMENT_IN_PROGRESS);
|
|
140
|
+
}
|
|
141
|
+
|
|
133
142
|
function main(
|
|
134
143
|
configuration /*: string */,
|
|
135
144
|
version /*: string */,
|
|
@@ -139,9 +148,16 @@ function main(
|
|
|
139
148
|
validateVersion(version);
|
|
140
149
|
|
|
141
150
|
if (!shouldReplaceRnCoreConfiguration(configuration)) {
|
|
151
|
+
// A fresh install leaves no marker; record the flavor we skipped on.
|
|
152
|
+
if (!fs.existsSync(LAST_BUILD_FILENAME)) {
|
|
153
|
+
updateLastBuildConfiguration(configuration);
|
|
154
|
+
}
|
|
142
155
|
return;
|
|
143
156
|
}
|
|
144
157
|
|
|
158
|
+
// Invalidate before touching the framework so an interrupted swap is
|
|
159
|
+
// detectable on the next run.
|
|
160
|
+
markReplacementInProgress();
|
|
145
161
|
replaceRNCoreConfiguration(configuration, version, podsRoot);
|
|
146
162
|
updateLastBuildConfiguration(configuration);
|
|
147
163
|
console.log('Done replacing React Native prebuilt');
|
|
@@ -44,8 +44,10 @@
|
|
|
44
44
|
* directing you to `--deintegrate`).
|
|
45
45
|
*
|
|
46
46
|
* Options:
|
|
47
|
-
* --version <ver> React Native version
|
|
48
|
-
*
|
|
47
|
+
* --version <ver> React Native version. Pinned into
|
|
48
|
+
* .spm-injected.json and reused by later runs
|
|
49
|
+
* until a new one is passed (default: the
|
|
50
|
+
* resolved node_modules/react-native version).
|
|
49
51
|
* --yes Skip the dirty-pbxproj confirmation prompt.
|
|
50
52
|
* [add] --xcodeproj <path> Which .xcodeproj to inject into (when several).
|
|
51
53
|
* [add] --product-name <name> Which app target to inject into (when several).
|
|
@@ -96,6 +98,7 @@ const {
|
|
|
96
98
|
cleanupLeftoverPodsGroup,
|
|
97
99
|
findInjectedXcodeproj,
|
|
98
100
|
injectSpmIntoExistingXcodeproj,
|
|
101
|
+
readArtifactsVersionOverride,
|
|
99
102
|
readPinnedConfigCommand,
|
|
100
103
|
removeSpmInjection,
|
|
101
104
|
} = require('./spm/generate-spm-xcodeproj');
|
|
@@ -153,7 +156,7 @@ function parseArgs(argv /*: Array<string> */) /*: SetupArgs */ {
|
|
|
153
156
|
.option('version', {
|
|
154
157
|
type: 'string',
|
|
155
158
|
describe:
|
|
156
|
-
'React Native version (e.g. 0.80.0). Defaults to the version in node_modules/react-native/package.json',
|
|
159
|
+
'React Native version (e.g. 0.80.0). Sticks: later runs reuse it until you pass a new one. Defaults to the version in node_modules/react-native/package.json',
|
|
157
160
|
})
|
|
158
161
|
.option('yes', {
|
|
159
162
|
type: 'boolean',
|
|
@@ -377,19 +380,31 @@ function resolveReactNativeRoot(
|
|
|
377
380
|
return reactNativeRoot;
|
|
378
381
|
}
|
|
379
382
|
|
|
383
|
+
// Explicit `--version` → the version an earlier `--version` pinned into the
|
|
384
|
+
// injection marker → node_modules/react-native/package.json. The pin makes
|
|
385
|
+
// `--version` stick for later flagless runs, which would otherwise re-point the
|
|
386
|
+
// project at a different artifact slot than the one it was wired to.
|
|
380
387
|
function determineVersion(
|
|
381
388
|
args /*: SetupArgs */,
|
|
382
389
|
reactNativeRoot /*: string */,
|
|
390
|
+
appRoot /*: string */,
|
|
383
391
|
) /*: string */ {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
392
|
+
if (args.version != null) {
|
|
393
|
+
return args.version;
|
|
394
|
+
}
|
|
395
|
+
const pinned = readArtifactsVersionOverride(appRoot);
|
|
396
|
+
if (pinned != null) {
|
|
397
|
+
log(
|
|
398
|
+
`Using version ${pinned} pinned in ${SPM_INJECTED_MARKER} by an earlier ` +
|
|
399
|
+
'--version. Pass --version to change it.',
|
|
389
400
|
);
|
|
390
|
-
|
|
401
|
+
return pinned;
|
|
391
402
|
}
|
|
392
|
-
|
|
403
|
+
// $FlowFixMe[incompatible-type] JSON.parse returns any
|
|
404
|
+
const pkgJson /*: {version: string} */ = JSON.parse(
|
|
405
|
+
fs.readFileSync(path.join(reactNativeRoot, 'package.json'), 'utf8'),
|
|
406
|
+
);
|
|
407
|
+
return pkgJson.version;
|
|
393
408
|
}
|
|
394
409
|
|
|
395
410
|
function runCodegenStep(
|
|
@@ -442,7 +457,7 @@ async function runScaffold(
|
|
|
442
457
|
// a comment — that's how SPM's manifest hash bumps on slot transitions.
|
|
443
458
|
let cacheSlotLabel /*: ?string */ = null;
|
|
444
459
|
try {
|
|
445
|
-
const rawVersion =
|
|
460
|
+
const rawVersion = determineVersion(args, reactNativeRoot, appRoot);
|
|
446
461
|
const slotVersion = await resolveCacheSlotVersion(rawVersion);
|
|
447
462
|
cacheSlotLabel = `${slotVersion}/dual-flavor`;
|
|
448
463
|
} catch {
|
|
@@ -1094,7 +1109,7 @@ async function main(argv /*:: ?: Array<string> */) /*: Promise<void> */ {
|
|
|
1094
1109
|
autolinkingConfigResult,
|
|
1095
1110
|
projectRoot,
|
|
1096
1111
|
);
|
|
1097
|
-
const version = determineVersion(args, reactNativeRoot);
|
|
1112
|
+
const version = determineVersion(args, reactNativeRoot, appRoot);
|
|
1098
1113
|
log(`React Native version: ${version}`);
|
|
1099
1114
|
|
|
1100
1115
|
// Resolve remote SPM mode ONCE up front. remotePackageConfig throws
|
|
@@ -1256,6 +1271,7 @@ if (require.main === module) {
|
|
|
1256
1271
|
module.exports = {
|
|
1257
1272
|
main,
|
|
1258
1273
|
detectStandardRnLayoutRedirect,
|
|
1274
|
+
determineVersion,
|
|
1259
1275
|
findInjectedXcodeproj,
|
|
1260
1276
|
generateAutolinkingConfigOrFailClosed,
|
|
1261
1277
|
parseArgs,
|
|
@@ -69,6 +69,14 @@
|
|
|
69
69
|
* // dir add/remove there re-triggers the sync. Relative/empty/non-string
|
|
70
70
|
* // entries are dropped with a warning; a non-array is ignored.
|
|
71
71
|
* watchPaths: ['/abs/path/Package.swift', '/abs/dir'],
|
|
72
|
+
* // Build-time shell phases on the app target — SwiftPM has no
|
|
73
|
+
* // `script_phase`. Only id/name/script are required; `id` is the
|
|
74
|
+
* // ledger key and UUID seed, `position` defaults to 'end'. Recorded
|
|
75
|
+
* // to `.spm-plugin-script-phases.json`, from which the `spm add`/
|
|
76
|
+
* // `update` injector emits a PBXShellScriptBuildPhase per entry.
|
|
77
|
+
* // Malformed entries and duplicate ids are fatal.
|
|
78
|
+
* scriptPhases: [{id, name, script, position, inputPaths, outputPaths,
|
|
79
|
+
* alwaysOutOfDate}],
|
|
72
80
|
* };
|
|
73
81
|
* };
|
|
74
82
|
*
|
|
@@ -76,12 +84,14 @@
|
|
|
76
84
|
* the merge, so regeneration stays deterministic and idempotent.
|
|
77
85
|
*/
|
|
78
86
|
|
|
87
|
+
const {isValidScriptPhaseId, isValidScriptPhaseName} = require('./spm-utils');
|
|
79
88
|
const path = require('path');
|
|
80
89
|
|
|
81
90
|
/*:: import type {
|
|
82
91
|
AutolinkedDep,
|
|
83
92
|
PluginContext,
|
|
84
93
|
PluginResult,
|
|
94
|
+
PluginScriptPhase,
|
|
85
95
|
DiscoveredPlugin,
|
|
86
96
|
} from './spm-types';
|
|
87
97
|
*/
|
|
@@ -149,6 +159,12 @@ function discoverPlugins(
|
|
|
149
159
|
return found;
|
|
150
160
|
}
|
|
151
161
|
|
|
162
|
+
const isNonEmptyString = (value /*: unknown */) /*: boolean */ =>
|
|
163
|
+
typeof value === 'string' && value.length > 0;
|
|
164
|
+
|
|
165
|
+
const isOptionalStringList = (value /*: unknown */) /*: boolean */ =>
|
|
166
|
+
value == null || (Array.isArray(value) && value.every(isNonEmptyString));
|
|
167
|
+
|
|
152
168
|
/**
|
|
153
169
|
* Invoke discovered plugins and merge their results. Each plugin gets the same
|
|
154
170
|
* context. Fail-closed: a throwing plugin aborts (named), and a malformed
|
|
@@ -169,10 +185,12 @@ function invokePlugins(
|
|
|
169
185
|
const flavoredFrameworks /*: Array<{id: string, frameworkName: string, linkage: 'dynamic', flavors: {debug: string, release: string}}> */ =
|
|
170
186
|
[];
|
|
171
187
|
const watchPaths /*: Array<string> */ = [];
|
|
188
|
+
const scriptPhases /*: Array<PluginScriptPhase> */ = [];
|
|
172
189
|
const seenPackages /*: Set<string> */ = new Set();
|
|
173
190
|
const seenProducts /*: Set<string> */ = new Set();
|
|
174
191
|
const seenFrameworkIds /*: Set<string> */ = new Set();
|
|
175
192
|
const seenFrameworkNames /*: Set<string> */ = new Set();
|
|
193
|
+
const seenScriptPhaseIds /*: Set<string> */ = new Set();
|
|
176
194
|
|
|
177
195
|
for (const {depName, pluginPath, plugin} of plugins) {
|
|
178
196
|
let result /*: unknown */ = null;
|
|
@@ -203,11 +221,18 @@ function invokePlugins(
|
|
|
203
221
|
const rawFrameworks = result.flavoredFrameworks ?? [];
|
|
204
222
|
// $FlowFixMe[incompatible-use]
|
|
205
223
|
const rawWatch = result.watchPaths ?? [];
|
|
224
|
+
// $FlowFixMe[incompatible-use]
|
|
225
|
+
const rawScriptPhases = result.scriptPhases ?? [];
|
|
206
226
|
if (!Array.isArray(rawFrameworks)) {
|
|
207
227
|
throw new Error(
|
|
208
228
|
`react-native spm: '${depName}' returned a non-array flavoredFrameworks.`,
|
|
209
229
|
);
|
|
210
230
|
}
|
|
231
|
+
if (!Array.isArray(rawScriptPhases)) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
`react-native spm: '${depName}' returned a non-array scriptPhases.`,
|
|
234
|
+
);
|
|
235
|
+
}
|
|
211
236
|
// Watch paths are best-effort staleness hints, so
|
|
212
237
|
// a non-array is ignored (warn, never fatal).
|
|
213
238
|
if (!Array.isArray(rawWatch)) {
|
|
@@ -314,6 +339,60 @@ function invokePlugins(
|
|
|
314
339
|
}
|
|
315
340
|
watchPaths.push(w);
|
|
316
341
|
}
|
|
342
|
+
// Fatal like flavoredFrameworks, not dropped like watchPaths: a missing
|
|
343
|
+
// phase produces a GREEN build whose generated content was never written.
|
|
344
|
+
for (const phase of rawScriptPhases) {
|
|
345
|
+
if (
|
|
346
|
+
phase == null ||
|
|
347
|
+
!isValidScriptPhaseId(phase.id) ||
|
|
348
|
+
!isNonEmptyString(phase.script) ||
|
|
349
|
+
(phase.position != null &&
|
|
350
|
+
phase.position !== 'beforeCompile' &&
|
|
351
|
+
phase.position !== 'end') ||
|
|
352
|
+
!isOptionalStringList(phase.inputPaths) ||
|
|
353
|
+
!isOptionalStringList(phase.outputPaths) ||
|
|
354
|
+
(phase.alwaysOutOfDate != null &&
|
|
355
|
+
typeof phase.alwaysOutOfDate !== 'boolean')
|
|
356
|
+
) {
|
|
357
|
+
const named = typeof phase?.id === 'string' ? ` '${phase.id}'` : '';
|
|
358
|
+
throw new Error(
|
|
359
|
+
`react-native spm: '${depName}' returned an invalid scriptPhase${named} ` +
|
|
360
|
+
'(need {id (matching /^[@A-Za-z0-9_./-]+$/, and not __proto__, ' +
|
|
361
|
+
'constructor or prototype), name, script} plus optional ' +
|
|
362
|
+
'{position: "beforeCompile" | "end", inputPaths, outputPaths, ' +
|
|
363
|
+
'alwaysOutOfDate}).',
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
if (!isValidScriptPhaseName(phase.name)) {
|
|
367
|
+
throw new Error(
|
|
368
|
+
`react-native spm: '${depName}' returned an invalid scriptPhase name for ` +
|
|
369
|
+
`'${phase.id}' (a name must be a non-empty single-line string — it is ` +
|
|
370
|
+
"the phase's display name in Xcode).",
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
if (seenScriptPhaseIds.has(phase.id)) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`react-native spm: duplicate script phase id '${phase.id}'.`,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
seenScriptPhaseIds.add(phase.id);
|
|
379
|
+
const normalized /*: PluginScriptPhase */ = {
|
|
380
|
+
id: phase.id,
|
|
381
|
+
name: phase.name,
|
|
382
|
+
script: phase.script,
|
|
383
|
+
position: phase.position ?? 'end',
|
|
384
|
+
};
|
|
385
|
+
if (phase.inputPaths != null) {
|
|
386
|
+
normalized.inputPaths = [...phase.inputPaths];
|
|
387
|
+
}
|
|
388
|
+
if (phase.outputPaths != null) {
|
|
389
|
+
normalized.outputPaths = [...phase.outputPaths];
|
|
390
|
+
}
|
|
391
|
+
if (phase.alwaysOutOfDate != null) {
|
|
392
|
+
normalized.alwaysOutOfDate = phase.alwaysOutOfDate;
|
|
393
|
+
}
|
|
394
|
+
scriptPhases.push(normalized);
|
|
395
|
+
}
|
|
317
396
|
}
|
|
318
397
|
|
|
319
398
|
return {
|
|
@@ -322,6 +401,7 @@ function invokePlugins(
|
|
|
322
401
|
generatedSources,
|
|
323
402
|
flavoredFrameworks,
|
|
324
403
|
watchPaths,
|
|
404
|
+
scriptPhases,
|
|
325
405
|
};
|
|
326
406
|
}
|
|
327
407
|
|