@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
|
@@ -36,6 +36,7 @@ import type {
|
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
38
|
const {
|
|
39
|
+
SpmNameCollisionError,
|
|
39
40
|
defaultReadConfig,
|
|
40
41
|
defaultResolveDep,
|
|
41
42
|
expandSpmDependencies,
|
|
@@ -43,6 +44,10 @@ const {
|
|
|
43
44
|
const {expandSpmSourceGlobs} = require('./generate-spm-autolinking');
|
|
44
45
|
const {readPodspec} = require('./read-podspec');
|
|
45
46
|
const {
|
|
47
|
+
REACT_CODEGEN_PACKAGE_NAME,
|
|
48
|
+
REACT_CODEGEN_PRODUCTS,
|
|
49
|
+
REACT_NATIVE_PACKAGE_NAME,
|
|
50
|
+
REACT_NATIVE_PRODUCTS,
|
|
46
51
|
SCAFFOLDER_MARKER,
|
|
47
52
|
makeLogger,
|
|
48
53
|
remotePackageConfig,
|
|
@@ -235,14 +240,18 @@ function translatePodspecToSpmTarget(
|
|
|
235
240
|
// `s.dependency "RNWorklets"` — a pod-style name the `react-native-*`
|
|
236
241
|
// heuristic can't recognize — to the right sibling package. Empty by default.
|
|
237
242
|
podToNpm /*: Map<string, string> */ = new Map(),
|
|
243
|
+
// npm name → resolved Swift name for every autolinked dep, so a sibling
|
|
244
|
+
// reference honors that sibling's `spm.name` instead of re-deriving it.
|
|
245
|
+
swiftNameByNpm /*: Map<string, string> */ = new Map(),
|
|
238
246
|
) /*: SpmScaffoldSpec */ {
|
|
239
247
|
const warnings = [...model.warnings];
|
|
240
248
|
|
|
241
|
-
// Swift target name:
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
249
|
+
// Swift target name: whatever the autolinker resolved for this dep — its
|
|
250
|
+
// `spm.name` override when set, else toSwiftName(npm-name). The autolinker
|
|
251
|
+
// registers the dep under that name in its aggregator (and in any sibling
|
|
252
|
+
// spm.dependencies refs), so the scaffolded Package.swift's product/library
|
|
253
|
+
// name must match it exactly — otherwise SPM resolution fails with a name
|
|
254
|
+
// mismatch on `.product(name: "X", package: "X")`.
|
|
246
255
|
//
|
|
247
256
|
// The podspec's `header_dir` is captured separately: when it changes the
|
|
248
257
|
// include surface (e.g. `<reanimated/...>` instead of `<ReactNativeReanimated/...>`),
|
|
@@ -251,10 +260,8 @@ function translatePodspecToSpmTarget(
|
|
|
251
260
|
// `<react/renderer/components/safeareacontext/...>` resolve through
|
|
252
261
|
// `-I common/cpp/`). Module-style includes that NEED the target name to
|
|
253
262
|
// match (e.g. reanimated's `<reanimated/X.h>` via SwiftPM's auto-generated
|
|
254
|
-
// module map)
|
|
255
|
-
|
|
256
|
-
// here.
|
|
257
|
-
const swiftName = toSwiftName(dep.name);
|
|
263
|
+
// module map) are what `spm.name` is for.
|
|
264
|
+
const swiftName = dep.swiftName ?? toSwiftName(dep.name);
|
|
258
265
|
|
|
259
266
|
// Header search paths — substitute Xcode build-setting tokens against the
|
|
260
267
|
// dep root. Anything we can't substitute is dropped + warned (avoids
|
|
@@ -512,6 +519,14 @@ function translatePodspecToSpmTarget(
|
|
|
512
519
|
);
|
|
513
520
|
}
|
|
514
521
|
|
|
522
|
+
const siblingSwiftNames /*: {[npmName: string]: string} */ = {};
|
|
523
|
+
for (const npmName of siblingNames) {
|
|
524
|
+
const resolved = swiftNameByNpm.get(npmName);
|
|
525
|
+
if (resolved != null) {
|
|
526
|
+
siblingSwiftNames[npmName] = resolved;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
515
530
|
return {
|
|
516
531
|
swiftName,
|
|
517
532
|
sources: expandedSources,
|
|
@@ -520,6 +535,7 @@ function translatePodspecToSpmTarget(
|
|
|
520
535
|
needsObjCPrefix,
|
|
521
536
|
coreReactNative,
|
|
522
537
|
siblingNames,
|
|
538
|
+
siblingSwiftNames,
|
|
523
539
|
extraFrameworks: model.frameworks,
|
|
524
540
|
weakFrameworks: model.weakFrameworks,
|
|
525
541
|
compilerFlags: model.compilerFlags,
|
|
@@ -652,7 +668,8 @@ function emitScaffoldedPackageSwift(
|
|
|
652
668
|
// the app by definition, so it stays a path reference — relative,
|
|
653
669
|
// computed at scaffold time.
|
|
654
670
|
const remote = ctx.remote;
|
|
655
|
-
const rnLabel =
|
|
671
|
+
const rnLabel =
|
|
672
|
+
remote != null ? remote.identity : REACT_NATIVE_PACKAGE_NAME;
|
|
656
673
|
const codegenDir = ctx.codegenPackageDir;
|
|
657
674
|
if (codegenDir == null) {
|
|
658
675
|
throw new Error(
|
|
@@ -670,24 +687,25 @@ function emitScaffoldedPackageSwift(
|
|
|
670
687
|
'emitScaffoldedPackageSwift: localXcfwPackageDir is required when no remote package is configured.',
|
|
671
688
|
);
|
|
672
689
|
}
|
|
673
|
-
packageDeps.push(
|
|
690
|
+
packageDeps.push(
|
|
691
|
+
`.package(name: "${REACT_NATIVE_PACKAGE_NAME}", path: "${xcfwDir}")`,
|
|
692
|
+
);
|
|
674
693
|
}
|
|
675
694
|
packageDeps.push(
|
|
676
|
-
`.package(name: "
|
|
677
|
-
);
|
|
678
|
-
targetDeps.push(`.product(name: "ReactHeaders", package: "${rnLabel}")`);
|
|
679
|
-
targetDeps.push(
|
|
680
|
-
`.product(name: "ReactNativeHeaders", package: "${rnLabel}")`,
|
|
681
|
-
);
|
|
682
|
-
targetDeps.push(
|
|
683
|
-
`.product(name: "ReactNativeDependenciesHeaders", package: "${rnLabel}")`,
|
|
684
|
-
);
|
|
685
|
-
targetDeps.push(
|
|
686
|
-
'.product(name: "ReactAppHeaders", package: "React-GeneratedCode")',
|
|
695
|
+
`.package(name: "${REACT_CODEGEN_PACKAGE_NAME}", path: "${codegenDir}")`,
|
|
687
696
|
);
|
|
697
|
+
for (const product of REACT_NATIVE_PRODUCTS) {
|
|
698
|
+
targetDeps.push(`.product(name: "${product}", package: "${rnLabel}")`);
|
|
699
|
+
}
|
|
700
|
+
for (const product of REACT_CODEGEN_PRODUCTS) {
|
|
701
|
+
targetDeps.push(
|
|
702
|
+
`.product(name: "${product}", package: "${REACT_CODEGEN_PACKAGE_NAME}")`,
|
|
703
|
+
);
|
|
704
|
+
}
|
|
688
705
|
}
|
|
689
706
|
for (const siblingName of spec.siblingNames) {
|
|
690
|
-
const swiftSibling =
|
|
707
|
+
const swiftSibling =
|
|
708
|
+
spec.siblingSwiftNames?.[siblingName] ?? toSwiftName(siblingName);
|
|
691
709
|
// The autolinker references each self-managed (scaffolded) dep through a
|
|
692
710
|
// `libs/<SwiftName>` symlink, and SPM resolves a manifest's relative
|
|
693
711
|
// package paths against that symlink location — so a sibling lives at
|
|
@@ -787,6 +805,10 @@ type ScaffoldContext = {
|
|
|
787
805
|
// podspec-name → npm-name index over all autolinked deps, so pod-style
|
|
788
806
|
// `s.dependency` names (e.g. "RNWorklets") wire to the right sibling.
|
|
789
807
|
podToNpm?: Map<string, string>,
|
|
808
|
+
// npm-name → resolved Swift name over all autolinked deps, so sibling
|
|
809
|
+
// references honor each sibling's `spm.name`.
|
|
810
|
+
swiftNameByNpm?: Map<string, string>,
|
|
811
|
+
remote: ?{url: string, version: string, identity: string},
|
|
790
812
|
};
|
|
791
813
|
*/
|
|
792
814
|
|
|
@@ -945,6 +967,7 @@ function scaffoldPackageSwiftForDep(
|
|
|
945
967
|
model,
|
|
946
968
|
dep,
|
|
947
969
|
ctx.podToNpm ?? new Map(),
|
|
970
|
+
ctx.swiftNameByNpm ?? new Map(),
|
|
948
971
|
);
|
|
949
972
|
|
|
950
973
|
// Mixed-language fail-closed: SPM can't compile Swift + C-family in one
|
|
@@ -999,7 +1022,7 @@ function scaffoldPackageSwiftForDep(
|
|
|
999
1022
|
.join('/');
|
|
1000
1023
|
const content = emitScaffoldedPackageSwift(spec, {
|
|
1001
1024
|
cacheSlotLabel: ctx.cacheSlotLabel,
|
|
1002
|
-
remote:
|
|
1025
|
+
remote: ctx.remote,
|
|
1003
1026
|
codegenPackageDir: relFromManifest('build', 'generated', 'ios'),
|
|
1004
1027
|
localXcfwPackageDir: relFromManifest('build', 'xcframeworks'),
|
|
1005
1028
|
});
|
|
@@ -1126,13 +1149,22 @@ function scaffoldAll(
|
|
|
1126
1149
|
directDeps.push({name, root, platforms: {ios: iosPlatform}});
|
|
1127
1150
|
}
|
|
1128
1151
|
|
|
1152
|
+
// Outside the try: a malformed remote config (RemoteVersionError) is a
|
|
1153
|
+
// misconfiguration to surface, not an expansion failure to degrade past.
|
|
1154
|
+
const remote = remotePackageConfig(appRoot);
|
|
1155
|
+
|
|
1129
1156
|
let allDeps /*: Array<AutolinkedDep> */ = [];
|
|
1130
1157
|
try {
|
|
1131
1158
|
allDeps = expandSpmDependencies(directDeps, {
|
|
1132
1159
|
readConfig: defaultReadConfig,
|
|
1133
1160
|
resolveDep: defaultResolveDep,
|
|
1161
|
+
extraReservedNames: remote != null ? [remote.identity] : undefined,
|
|
1162
|
+
log,
|
|
1134
1163
|
});
|
|
1135
1164
|
} catch (e) {
|
|
1165
|
+
if (e instanceof SpmNameCollisionError) {
|
|
1166
|
+
throw e;
|
|
1167
|
+
}
|
|
1136
1168
|
// A transitive-resolution failure shouldn't abort the whole scaffold pass;
|
|
1137
1169
|
// fall back to the direct deps so at least those get manifests.
|
|
1138
1170
|
log(`Transitive spm.dependencies expansion failed: ${e.message}`);
|
|
@@ -1167,6 +1199,13 @@ function scaffoldAll(
|
|
|
1167
1199
|
}
|
|
1168
1200
|
}
|
|
1169
1201
|
|
|
1202
|
+
const swiftNameByNpm /*: Map<string, string> */ = new Map();
|
|
1203
|
+
for (const dep of allDeps) {
|
|
1204
|
+
if (dep.swiftName != null) {
|
|
1205
|
+
swiftNameByNpm.set(dep.name, dep.swiftName);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1170
1209
|
const ctx /*: ScaffoldContext */ = {
|
|
1171
1210
|
appRoot,
|
|
1172
1211
|
projectRoot,
|
|
@@ -1175,6 +1214,8 @@ function scaffoldAll(
|
|
|
1175
1214
|
dryRun: opts.dryRun === true,
|
|
1176
1215
|
cacheSlotLabel: opts.cacheSlotLabel ?? null,
|
|
1177
1216
|
podToNpm,
|
|
1217
|
+
swiftNameByNpm,
|
|
1218
|
+
remote,
|
|
1178
1219
|
};
|
|
1179
1220
|
const skipSet /*: Set<string> */ = new Set(opts.skipDeps ?? []);
|
|
1180
1221
|
|
|
@@ -28,13 +28,55 @@ function generateUUID(seed /*: string */) /*: string */ {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Escapes a string for OpenStep plist format if needed.
|
|
31
|
+
* Escapes a string for OpenStep plist format if needed. A literal CR or tab in
|
|
32
|
+
* a quoted value is legal but Xcode rewrites it to its escape on the next save,
|
|
33
|
+
* planting a spurious diff in the user's repo — so escape those too (a plugin
|
|
34
|
+
* `script` with CRLF line endings is how one gets in).
|
|
32
35
|
*/
|
|
33
36
|
function quoteIfNeeded(s /*: string */) /*: string */ {
|
|
34
37
|
if (/^[a-zA-Z0-9._/]+$/.test(s)) {
|
|
35
38
|
return s;
|
|
36
39
|
}
|
|
37
|
-
return `"${s
|
|
40
|
+
return `"${s
|
|
41
|
+
.replace(/\\/g, '\\\\')
|
|
42
|
+
.replace(/"/g, '\\"')
|
|
43
|
+
.replace(/\n/g, '\\n')
|
|
44
|
+
.replace(/\r/g, '\\r')
|
|
45
|
+
.replace(/\t/g, '\\t')}"`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Normalize an UNTRUSTED label (a plugin's script-phase name, say) for use
|
|
50
|
+
* inside a pbxproj `/* … */` comment. Such a comment is purely cosmetic — Xcode
|
|
51
|
+
* regenerates it from the object's own fields — while the text AROUND it is
|
|
52
|
+
* scanned by delimiter, so every character that could be read as structure (or
|
|
53
|
+
* split the line) becomes a space, and runs of whitespace collapse:
|
|
54
|
+
* `findObjectByUuid` takes the first `{` after a UUID as that object's body,
|
|
55
|
+
* `removeArrayMembersByUuid` identifies a member line by its trailing comma, and
|
|
56
|
+
* `scanToClose` treats a `"` as opening a string. Stable (same input ⇒ same
|
|
57
|
+
* output), so re-syncing stays byte-identical. Returns '' when nothing survives;
|
|
58
|
+
* callers supply the fallback label.
|
|
59
|
+
*
|
|
60
|
+
* Comments RN writes to Xcode's OWN convention (`XCLocalSwiftPackageReference
|
|
61
|
+
* "build/xcframeworks"`, `<file> in Sources`) are NOT run through this: they are
|
|
62
|
+
* fixed strings, and normalizing them would rewrite bytes Xcode itself produces —
|
|
63
|
+
* a spurious diff in the user's repo on their next save.
|
|
64
|
+
*/
|
|
65
|
+
function commentSafe(s /*: string */) /*: string */ {
|
|
66
|
+
return s
|
|
67
|
+
.replace(/[{}(),;="*/\s]/g, ' ')
|
|
68
|
+
.replace(/ +/g, ' ')
|
|
69
|
+
.trim();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Format the ` /* … */` suffix pbxproj writes after a UUID, omitted entirely for
|
|
74
|
+
* an empty label (an uncommented UUID is well-formed — Xcode writes those itself
|
|
75
|
+
* for unnamed groups). Labels that did not originate in this repo must already
|
|
76
|
+
* have been through `commentSafe`.
|
|
77
|
+
*/
|
|
78
|
+
function uuidComment(comment /*: ?string */) /*: string */ {
|
|
79
|
+
return comment != null && comment !== '' ? ` /* ${comment} */` : '';
|
|
38
80
|
}
|
|
39
81
|
|
|
40
82
|
/**
|
|
@@ -47,11 +89,7 @@ function quoteIfNeeded(s /*: string */) /*: string */ {
|
|
|
47
89
|
function serializeEntry(
|
|
48
90
|
entry /*: {readonly uuid: string, readonly comment?: ?string, readonly fields: {readonly [string]: string}, ...} */,
|
|
49
91
|
) /*: string */ {
|
|
50
|
-
|
|
51
|
-
entry.comment != null && entry.comment !== ''
|
|
52
|
-
? ` /* ${entry.comment} */`
|
|
53
|
-
: '';
|
|
54
|
-
let out = `\t\t${entry.uuid}${comment} = {`;
|
|
92
|
+
let out = `\t\t${entry.uuid}${uuidComment(entry.comment)} = {`;
|
|
55
93
|
const fieldKeys = Object.keys(entry.fields);
|
|
56
94
|
if (
|
|
57
95
|
fieldKeys.length <= 3 &&
|
|
@@ -343,8 +381,7 @@ function addArrayMembers(
|
|
|
343
381
|
const memberIndent = fieldIndent + '\t';
|
|
344
382
|
const line = (
|
|
345
383
|
m /*: {readonly uuid: string, readonly comment?: ?string, ...} */,
|
|
346
|
-
) =>
|
|
347
|
-
`${memberIndent}${m.uuid}${m.comment != null && m.comment !== '' ? ` /* ${m.comment} */` : ''},\n`;
|
|
384
|
+
) => `${memberIndent}${m.uuid}${uuidComment(m.comment)},\n`;
|
|
348
385
|
|
|
349
386
|
const field = findField(text, obj, key);
|
|
350
387
|
if (field != null) {
|
|
@@ -367,6 +404,38 @@ function addArrayMembers(
|
|
|
367
404
|
return text.slice(0, obj.bodyOpen + 1) + block + text.slice(obj.bodyOpen + 1);
|
|
368
405
|
}
|
|
369
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Indices of the `,` separators at an array's top level — a comma inside a
|
|
409
|
+
* quoted member (`"$(FOO),weird"`) separates nothing.
|
|
410
|
+
*/
|
|
411
|
+
function topLevelCommas(inner /*: string */) /*: Array<number> */ {
|
|
412
|
+
const out = [];
|
|
413
|
+
for (let i = 0; i < inner.length; i++) {
|
|
414
|
+
if (inner[i] === '"') {
|
|
415
|
+
i = scanString(inner, i);
|
|
416
|
+
} else if (inner[i] === ',') {
|
|
417
|
+
out.push(i);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return out;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* The members of an array's inner text (what sits between its parens), trimmed
|
|
425
|
+
* and with empty slots — e.g. the one a trailing comma leaves — dropped. A bare
|
|
426
|
+
* scalar value parses as its own single member.
|
|
427
|
+
*/
|
|
428
|
+
function arrayMembers(inner /*: string */) /*: Array<string> */ {
|
|
429
|
+
const members = [];
|
|
430
|
+
let start = 0;
|
|
431
|
+
for (const comma of topLevelCommas(inner)) {
|
|
432
|
+
members.push(inner.slice(start, comma));
|
|
433
|
+
start = comma + 1;
|
|
434
|
+
}
|
|
435
|
+
members.push(inner.slice(start));
|
|
436
|
+
return members.map(m => m.trim()).filter(m => m !== '');
|
|
437
|
+
}
|
|
438
|
+
|
|
370
439
|
/**
|
|
371
440
|
* Append raw string values to a `( … )` array build-setting (e.g.
|
|
372
441
|
* OTHER_LDFLAGS), deduping by exact token. Creates the setting seeded with
|
|
@@ -385,31 +454,57 @@ function addArrayStringValues(
|
|
|
385
454
|
|
|
386
455
|
const field = findField(text, obj, key);
|
|
387
456
|
if (field != null) {
|
|
457
|
+
const isArray = field.value.trimStart().startsWith('(');
|
|
458
|
+
const openParen = isArray
|
|
459
|
+
? field.valueStart + field.value.indexOf('(')
|
|
460
|
+
: -1;
|
|
461
|
+
const closeParen = isArray ? scanToClose(text, openParen) : -1;
|
|
462
|
+
const inner = isArray ? text.slice(openParen + 1, closeParen) : field.value;
|
|
388
463
|
// Dedup by EXACT existing member, not substring — a substring check would
|
|
389
464
|
// treat `"-ObjC"` as already present when only `"-ObjCFoo"` is there (and
|
|
390
|
-
// vice-versa).
|
|
391
|
-
const existingMembers = new Set(
|
|
392
|
-
field.value
|
|
393
|
-
.replace(/^\s*\(/, '')
|
|
394
|
-
.replace(/\)\s*$/, '')
|
|
395
|
-
.split(',')
|
|
396
|
-
.map(s => s.trim())
|
|
397
|
-
.filter(s => s.length > 0),
|
|
398
|
-
);
|
|
465
|
+
// vice-versa).
|
|
466
|
+
const existingMembers = new Set(arrayMembers(inner));
|
|
399
467
|
const fresh = values.filter(v => !existingMembers.has(v));
|
|
400
468
|
if (fresh.length === 0) {
|
|
401
469
|
return text;
|
|
402
470
|
}
|
|
403
|
-
if (
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
471
|
+
if (isArray) {
|
|
472
|
+
if (inner.includes('\n')) {
|
|
473
|
+
// Multi-line array — one member per line, before the closing `)`.
|
|
474
|
+
const lineStart = text.lastIndexOf('\n', closeParen) + 1;
|
|
475
|
+
const lines = fresh.map(v => `${memberIndent}${v},\n`).join('');
|
|
476
|
+
return text.slice(0, lineStart) + lines + text.slice(lineStart);
|
|
477
|
+
}
|
|
478
|
+
// One-line array (hand-edited projects, XcodeGen, Tuist) — splice the
|
|
479
|
+
// members in ahead of the `)`, in the separator style already there.
|
|
480
|
+
// Reformatting it multi-line instead would have to record the old shape
|
|
481
|
+
// to stay reversible on deinit.
|
|
482
|
+
const commas = topLevelCommas(inner);
|
|
483
|
+
const gapMatch =
|
|
484
|
+
commas.length > 0 ? /^[\t ]*/.exec(inner.slice(commas[0] + 1)) : null;
|
|
485
|
+
const gap = gapMatch != null ? gapMatch[0] : ' ';
|
|
486
|
+
const core = inner.replace(/[\t ]+$/, '');
|
|
487
|
+
const joined = fresh.join(`,${gap}`);
|
|
488
|
+
const insertion =
|
|
489
|
+
core === ''
|
|
490
|
+
? joined
|
|
491
|
+
: core.endsWith(',')
|
|
492
|
+
? `${gap}${joined},`
|
|
493
|
+
: `,${gap}${joined}`;
|
|
494
|
+
const at = openParen + 1 + core.length;
|
|
495
|
+
return text.slice(0, at) + insertion + text.slice(at);
|
|
408
496
|
}
|
|
409
|
-
// Existing scalar — promote to an array preserving the prior value.
|
|
497
|
+
// Existing scalar — promote to an array preserving the prior value. Skip it
|
|
498
|
+
// when it IS the `"$(inherited)"` the array is seeded with (emitted twice),
|
|
499
|
+
// or when it is empty (a bare `,` is not a valid plist element). The seed
|
|
500
|
+
// test unquotes first: pbxproj accepts `$(inherited)` bare, and Xcode's own
|
|
501
|
+
// quoted form is the same value to the build system.
|
|
502
|
+
const prior = field.value.trim();
|
|
503
|
+
const carriesPrior =
|
|
504
|
+
prior !== '' && prior.replace(/^"(.*)"$/s, '$1') !== '$(inherited)';
|
|
410
505
|
const replacement = arrayBlock([
|
|
411
506
|
'"$(inherited)"',
|
|
412
|
-
|
|
507
|
+
...(carriesPrior ? [prior] : []),
|
|
413
508
|
...fresh,
|
|
414
509
|
]);
|
|
415
510
|
return (
|
|
@@ -464,7 +559,12 @@ function setScalarField(
|
|
|
464
559
|
// ---------------------------------------------------------------------------
|
|
465
560
|
// Surgical removal — the inverse of the additive helpers above. `deinit` uses
|
|
466
561
|
// these to undo exactly what injection added, leaving every other byte (incl.
|
|
467
|
-
// user edits made after injection) untouched.
|
|
562
|
+
// user edits made after injection) untouched. The exception is a scalar that
|
|
563
|
+
// injection promoted to an array: reversing that rewrites the whole field (see
|
|
564
|
+
// removeRecordedBuildSettings), so members added to it afterwards are lost.
|
|
565
|
+
// That is not deinit-only — every re-sync reverts from the recorded baseline
|
|
566
|
+
// before re-injecting, so an `spm update` discards them just the same.
|
|
567
|
+
// All are pure string transforms.
|
|
468
568
|
// ---------------------------------------------------------------------------
|
|
469
569
|
|
|
470
570
|
/**
|
|
@@ -495,7 +595,8 @@ function removeObjectByUuid(
|
|
|
495
595
|
* `uuids` from every `( … )` list in the file (packageReferences,
|
|
496
596
|
* packageProductDependencies, a Frameworks phase's `files`, buildPhases, …).
|
|
497
597
|
* Only matches member lines (trailing comma), never the object-definition line
|
|
498
|
-
* (which ends in `= {`), so it composes safely with removeObjectByUuid
|
|
598
|
+
* (which ends in `= {`), so it composes safely with removeObjectByUuid — which
|
|
599
|
+
* holds because no comment can contain a comma (see commentSafe).
|
|
499
600
|
*/
|
|
500
601
|
function removeArrayMembersByUuid(
|
|
501
602
|
text /*: string */,
|
|
@@ -525,6 +626,33 @@ function removeField(
|
|
|
525
626
|
return text.slice(0, f.matchStart) + text.slice(f.tokenEnd + 1);
|
|
526
627
|
}
|
|
527
628
|
|
|
629
|
+
/**
|
|
630
|
+
* Drop one member from an array field's value text. The patterns mirror the
|
|
631
|
+
* forms addArrayStringValues inserts, tried in the order that makes the span
|
|
632
|
+
* removed exactly the span it added: a line of its own (multi-line array), then
|
|
633
|
+
* after a comma, before a comma, or alone ahead of the `)` (the one-line
|
|
634
|
+
* shapes). Each is anchored on a delimiter, so a value that is merely a prefix
|
|
635
|
+
* of a longer member is never mistaken for it.
|
|
636
|
+
*/
|
|
637
|
+
function removeArrayMember(
|
|
638
|
+
region /*: string */,
|
|
639
|
+
value /*: string */,
|
|
640
|
+
) /*: string */ {
|
|
641
|
+
const v = escapeRegExp(value);
|
|
642
|
+
for (const pattern of [
|
|
643
|
+
`\\n[\\t ]*${v},`,
|
|
644
|
+
`,[\\t ]*${v}(?=[\\t ]*[,)])`,
|
|
645
|
+
`[\\t ]*${v},[\\t ]*`,
|
|
646
|
+
`[\\t ]*${v}(?=[\\t ]*\\))`,
|
|
647
|
+
]) {
|
|
648
|
+
const shorter = region.replace(new RegExp(pattern), '');
|
|
649
|
+
if (shorter !== region) {
|
|
650
|
+
return shorter;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return region;
|
|
654
|
+
}
|
|
655
|
+
|
|
528
656
|
/**
|
|
529
657
|
* Remove specific raw string members from an existing `( … )` array field
|
|
530
658
|
* (inverse of addArrayStringValues' append branch). Leaves the field and any
|
|
@@ -542,7 +670,7 @@ function removeArrayStringValues(
|
|
|
542
670
|
}
|
|
543
671
|
let region = text.slice(f.valueStart, f.tokenEnd);
|
|
544
672
|
for (const val of values) {
|
|
545
|
-
region = region
|
|
673
|
+
region = removeArrayMember(region, val);
|
|
546
674
|
}
|
|
547
675
|
return text.slice(0, f.valueStart) + region + text.slice(f.tokenEnd);
|
|
548
676
|
}
|
|
@@ -628,6 +756,8 @@ module.exports = {
|
|
|
628
756
|
namespacedUUID,
|
|
629
757
|
serializeEntry,
|
|
630
758
|
quoteIfNeeded,
|
|
759
|
+
commentSafe,
|
|
760
|
+
uuidComment,
|
|
631
761
|
// Surgical-edit toolkit (in-place injection):
|
|
632
762
|
scanString,
|
|
633
763
|
scanToClose,
|
package/scripts/spm/spm-types.js
CHANGED
|
@@ -168,7 +168,6 @@ export type SpmModuleConfig = {
|
|
|
168
168
|
name: string,
|
|
169
169
|
path: string,
|
|
170
170
|
exclude?: Array<string>,
|
|
171
|
-
publicHeadersPath?: ?string,
|
|
172
171
|
// Optional CocoaPods-style glob allowlist (analog of s.source_files).
|
|
173
172
|
// When set, replaces auto source discovery for the module — only files
|
|
174
173
|
// matching one of these patterns are passed to SPM via `sources:`.
|
|
@@ -219,6 +218,21 @@ export type PluginProductDep = {name: string, package: string};
|
|
|
219
218
|
// in the codegen package so it compiles.
|
|
220
219
|
export type PluginGeneratedSource = {path: string};
|
|
221
220
|
|
|
221
|
+
// A build-time shell phase for the app target — SwiftPM's missing analog of
|
|
222
|
+
// CocoaPods' `script_phase`. `id` is the stable ledger key and deterministic
|
|
223
|
+
// UUID seed (charset /^[@A-Za-z0-9_./-]+$/, so a scoped npm name works; `:` is
|
|
224
|
+
// excluded because the seed is `plugin:<id>`); `position` is normalized to 'end'
|
|
225
|
+
// by invokePlugins so consumers never re-derive the default.
|
|
226
|
+
export type PluginScriptPhase = {
|
|
227
|
+
id: string,
|
|
228
|
+
name: string,
|
|
229
|
+
script: string,
|
|
230
|
+
position: 'beforeCompile' | 'end',
|
|
231
|
+
inputPaths?: Array<string>,
|
|
232
|
+
outputPaths?: Array<string>,
|
|
233
|
+
alwaysOutOfDate?: boolean,
|
|
234
|
+
};
|
|
235
|
+
|
|
222
236
|
// A plugin-declared dynamic XCFramework pair. RN validates both paths, stages
|
|
223
237
|
// immutable app-local slots, and links/embeds the selected framework outside
|
|
224
238
|
// SwiftPM.
|
|
@@ -302,6 +316,10 @@ export type PluginResult = {
|
|
|
302
316
|
// for staleness, e.g. the plugin dep's own `Package.swift` and per-module
|
|
303
317
|
// manifests. Folded into `.spm-sync-watch-paths` by main().
|
|
304
318
|
watchPaths: Array<string>,
|
|
319
|
+
// Build-time shell phases for the app target, recorded to
|
|
320
|
+
// `.spm-plugin-script-phases.json` by main() and injected by `spm add`/
|
|
321
|
+
// `update`.
|
|
322
|
+
scriptPhases: Array<PluginScriptPhase>,
|
|
305
323
|
};
|
|
306
324
|
|
|
307
325
|
export type SpmAutolinkingPlugin = (context: PluginContext) => ?{
|
|
@@ -310,6 +328,11 @@ export type SpmAutolinkingPlugin = (context: PluginContext) => ?{
|
|
|
310
328
|
generatedSources?: Array<PluginGeneratedSource>,
|
|
311
329
|
flavoredFrameworks?: Array<PluginFlavoredFramework>,
|
|
312
330
|
watchPaths?: Array<string>,
|
|
331
|
+
// `position` may be omitted here; invokePlugins normalizes it to 'end'.
|
|
332
|
+
scriptPhases?: Array<{
|
|
333
|
+
...PluginScriptPhase,
|
|
334
|
+
position?: 'beforeCompile' | 'end',
|
|
335
|
+
}>,
|
|
313
336
|
};
|
|
314
337
|
|
|
315
338
|
export type DiscoveredPlugin = {
|
|
@@ -458,10 +481,13 @@ export type SpmScaffoldSpec = {
|
|
|
458
481
|
// Bucketed dependency references — pre-computed by the translation layer.
|
|
459
482
|
// `coreReactNative` is true when ANY React-* / RCT* / RCT-Folly / glog
|
|
460
483
|
// dep is present (so we add React's invariant header products).
|
|
461
|
-
// `siblingNames` are npm names that match other autolinked deps
|
|
462
|
-
//
|
|
484
|
+
// `siblingNames` are npm names that match other autolinked deps.
|
|
485
|
+
// `siblingSwiftNames` carries each one's resolved Swift name (honoring the
|
|
486
|
+
// sibling's `spm.name`); a sibling absent from it falls back to
|
|
487
|
+
// toSwiftName(npmName) at emit time.
|
|
463
488
|
coreReactNative: boolean,
|
|
464
489
|
siblingNames: Array<string>,
|
|
490
|
+
siblingSwiftNames?: {[npmName: string]: string},
|
|
465
491
|
// Extra frameworks beyond the autolinker's default UIKit/Foundation/CoreGraphics
|
|
466
492
|
// set. Merged with the defaults at emit time.
|
|
467
493
|
extraFrameworks: Array<string>,
|