Skip to content

Commit 33d44b5

Browse files
authored
fix: allow app spm to "override" plugin spm packages (#5951)
1 parent 9013634 commit 33d44b5

File tree

2 files changed

+185
-2
lines changed

2 files changed

+185
-2
lines changed

lib/services/ios/spm-service.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,29 @@ export class SPMService implements ISPMService {
2424
return spmPackages;
2525
}
2626

27+
/**
28+
* Merges plugin SPM packages with app SPM packages.
29+
* App packages take precedence over plugin packages with the same name.
30+
* @param appPackages - Array of app SPM packages (modified in place)
31+
* @param pluginPackages - Array of plugin SPM packages to merge
32+
*/
33+
private mergePluginSPMPackages(
34+
appPackages: IosSPMPackage[],
35+
pluginPackages: IosSPMPackage[],
36+
): void {
37+
// include swift packages from plugin configs
38+
// but allow app packages to override plugin packages with the same name
39+
const appPackageNames = new Set(appPackages.map(pkg => pkg.name));
40+
41+
for (const pluginPkg of pluginPackages) {
42+
if (appPackageNames.has(pluginPkg.name)) {
43+
this.$logger.trace(`SPM: app package overrides plugin package: ${pluginPkg.name}`);
44+
} else {
45+
appPackages.push(pluginPkg);
46+
}
47+
}
48+
}
49+
2750
// note: this is not used anywhere at the moment.
2851
// public hasSPMPackages(projectData: IProjectData): boolean {
2952
// return this.getSPMPackages(projectData).length > 0;
@@ -41,8 +64,7 @@ export class SPMService implements ISPMService {
4164
);
4265

4366
if (pluginSpmPackages?.length) {
44-
// include swift packages from plugin configs
45-
spmPackages.push(...pluginSpmPackages);
67+
this.mergePluginSPMPackages(spmPackages, pluginSpmPackages);
4668
}
4769

4870
if (!spmPackages.length) {

test/spm-service.ts

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { assert } from "chai";
2+
3+
/**
4+
* Helper function to merge app and plugin SPM packages.
5+
* App packages take precedence over plugin packages with the same name.
6+
*/
7+
function mergeSPMPackages(appPackages: any[], pluginPackages: any[]): any[] {
8+
const spmPackages = [...appPackages];
9+
const appPackageNames = new Set(spmPackages.map(pkg => pkg.name));
10+
11+
for (const pluginPkg of pluginPackages) {
12+
if (!appPackageNames.has(pluginPkg.name)) {
13+
spmPackages.push(pluginPkg);
14+
}
15+
}
16+
17+
return spmPackages;
18+
}
19+
20+
describe("SPM Service - Package Override Logic", () => {
21+
describe("merging app and plugin SPM packages", () => {
22+
it("should allow app packages to override plugin packages with the same name", () => {
23+
// This test validates the merge logic without requiring MobileProject
24+
const appPackages = [
25+
{
26+
name: "FirebaseCore",
27+
repositoryURL: "https://github.com/firebase/firebase-ios-sdk",
28+
version: "10.0.0",
29+
libs: ["FirebaseCore"],
30+
},
31+
];
32+
33+
const pluginPackages = [
34+
{
35+
name: "FirebaseCore",
36+
repositoryURL: "https://github.com/firebase/firebase-ios-sdk",
37+
version: "9.0.0",
38+
libs: ["FirebaseCore"],
39+
},
40+
{
41+
name: "Alamofire",
42+
repositoryURL: "https://github.com/Alamofire/Alamofire",
43+
version: "5.0.0",
44+
libs: ["Alamofire"],
45+
},
46+
];
47+
48+
const spmPackages = mergeSPMPackages(appPackages, pluginPackages);
49+
50+
// Verify the result
51+
assert.equal(spmPackages.length, 2, "Should have 2 packages total");
52+
53+
const firebasePackage = spmPackages.find((pkg) => pkg.name === "FirebaseCore");
54+
assert.isDefined(firebasePackage, "Should include FirebaseCore package");
55+
assert.equal(
56+
firebasePackage.version,
57+
"10.0.0",
58+
"Should use app's FirebaseCore version (10.0.0), not plugin's (9.0.0)",
59+
);
60+
61+
const alamofirePackage = spmPackages.find((pkg) => pkg.name === "Alamofire");
62+
assert.isDefined(alamofirePackage, "Should include Alamofire package from plugin");
63+
assert.equal(alamofirePackage.version, "5.0.0", "Should use plugin's Alamofire version");
64+
});
65+
66+
it("should include all plugin packages when no app packages exist", () => {
67+
const appPackages: any[] = [];
68+
69+
const pluginPackages = [
70+
{
71+
name: "FirebaseCore",
72+
repositoryURL: "https://github.com/firebase/firebase-ios-sdk",
73+
version: "9.0.0",
74+
libs: ["FirebaseCore"],
75+
},
76+
{
77+
name: "Alamofire",
78+
repositoryURL: "https://github.com/Alamofire/Alamofire",
79+
version: "5.0.0",
80+
libs: ["Alamofire"],
81+
},
82+
];
83+
84+
const spmPackages = mergeSPMPackages(appPackages, pluginPackages);
85+
86+
// Verify the result
87+
assert.equal(spmPackages.length, 2, "Should include both plugin packages");
88+
89+
const packageNames = spmPackages.map((pkg) => pkg.name);
90+
assert.include(packageNames, "FirebaseCore", "Should include FirebaseCore");
91+
assert.include(packageNames, "Alamofire", "Should include Alamofire");
92+
});
93+
94+
it("should handle local packages override correctly", () => {
95+
const appPackages = [
96+
{
97+
name: "LocalSDK",
98+
path: "./custom-sdk",
99+
libs: ["LocalSDK"],
100+
},
101+
];
102+
103+
const pluginPackages = [
104+
{
105+
name: "LocalSDK",
106+
path: "./plugin-sdk",
107+
libs: ["LocalSDK"],
108+
},
109+
];
110+
111+
const spmPackages = mergeSPMPackages(appPackages, pluginPackages);
112+
113+
// Verify the result
114+
assert.equal(spmPackages.length, 1, "Should have exactly 1 package");
115+
116+
const localPackage = spmPackages.find((pkg) => pkg.name === "LocalSDK");
117+
assert.isDefined(localPackage, "Should include LocalSDK package");
118+
assert.equal(
119+
(localPackage as any).path,
120+
"./custom-sdk",
121+
"Should use app's LocalSDK path, not plugin's",
122+
);
123+
});
124+
125+
it("should keep all packages when there are no name conflicts", () => {
126+
const appPackages = [
127+
{
128+
name: "FirebaseCore",
129+
repositoryURL: "https://github.com/firebase/firebase-ios-sdk",
130+
version: "10.0.0",
131+
libs: ["FirebaseCore"],
132+
},
133+
];
134+
135+
const pluginPackages = [
136+
{
137+
name: "Alamofire",
138+
repositoryURL: "https://github.com/Alamofire/Alamofire",
139+
version: "5.0.0",
140+
libs: ["Alamofire"],
141+
},
142+
{
143+
name: "Kingfisher",
144+
repositoryURL: "https://github.com/onevcat/Kingfisher",
145+
version: "7.0.0",
146+
libs: ["Kingfisher"],
147+
},
148+
];
149+
150+
const spmPackages = mergeSPMPackages(appPackages, pluginPackages);
151+
152+
// Verify the result
153+
assert.equal(spmPackages.length, 3, "Should have all 3 packages");
154+
155+
const packageNames = spmPackages.map((pkg) => pkg.name);
156+
assert.include(packageNames, "FirebaseCore");
157+
assert.include(packageNames, "Alamofire");
158+
assert.include(packageNames, "Kingfisher");
159+
});
160+
});
161+
});

0 commit comments

Comments
 (0)