Skip to content

Commit 3a5801c

Browse files
committed
fix(ios): deal with the latest cli breaking change
I've also: * made platform props access lazy * moved the important cli code to ts * added required arguments checks * fixed some test snapshots * exposed the package types
1 parent 599b297 commit 3a5801c

File tree

11 files changed

+180
-74
lines changed

11 files changed

+180
-74
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-cli-bump-version",
3-
"version": "1.4.0",
3+
"version": "1.5.0",
44
"main": "src/index.ts",
55
"types": "lib/index.d.ts",
66
"license": "MIT",

react-native.config.js

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,7 @@ module.exports = {
55
commands: [{
66
name: "bump-version",
77
func: (_, config, args) => {
8-
if (args.skipCodeFor === "all" && args.skipSemverFor === "all") {
9-
// https://i.kym-cdn.com/photos/images/newsfeed/001/240/075/90f.png
10-
console.log("My work here is done.");
11-
return;
12-
}
13-
14-
const appGradlePath = path.join(
15-
config.project.android.sourceDir,
16-
config.project.android.appName,
17-
"build.gradle",
18-
);
19-
20-
versioner({
21-
root: config.root,
22-
pbxprojPath: config.project.ios.pbxprojPath,
23-
buildGradlePath: appGradlePath,
24-
type: args.type,
25-
semver: args.semver,
26-
skipCodeFor: args.skipCodeFor
27-
? args.skipCodeFor.split(" ")
28-
: [],
29-
skipSemVerFor: args.skipSemverFor
30-
? args.skipSemverFor.split(" ")
31-
: [],
32-
});
8+
versioner(config, args).run();
339
},
3410
options: [
3511
{

src/index.ts

Lines changed: 109 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ type Configs = {
1313
skipSemVerFor: Platforms[];
1414
skipCodeFor: Platforms[];
1515
root: string;
16-
pbxprojPath: string;
17-
buildGradlePath: string;
16+
pbxprojPath: () => string;
17+
buildGradlePath: () => string;
1818
};
1919

2020
// -- FP
@@ -30,7 +30,7 @@ const pipe2 = <A1, A2, R>(
3030

3131
const replace = (expr: string | RegExp, replacement: string, str: string) => {
3232
return str.replace(expr, replacement);
33-
}
33+
};
3434

3535
// -- Specializations
3636

@@ -42,11 +42,12 @@ const writeFile = (fPath: string, file: string) => {
4242
fs.writeFileSync(fPath, file, "utf8");
4343
};
4444

45-
const matchFirst = (reg: RegExp) => (value: string) => {
46-
const [, first] = ([] as string[]).concat(reg.exec(value)!);
45+
const matchFirst = (reg: RegExp) =>
46+
(value: string) => {
47+
const [, first] = ([] as string[]).concat(reg.exec(value)!);
4748

48-
return first;
49-
}
49+
return first;
50+
};
5051

5152
const incrementSemVer = (current: string, type: SemVer | undefined) => {
5253
const [major, minor, patch] = parseSemVer(current);
@@ -66,28 +67,27 @@ const incrementSemVer = (current: string, type: SemVer | undefined) => {
6667
throw new Error(`'${type}' is not a semver type`);
6768
};
6869

69-
7070
// -- Managers
7171

7272
abstract class BaseFileManager {
73-
private readonly basePath: string;
73+
private readonly basePath: () => string;
7474
protected content: string | null = null;
7575

76-
constructor(basePath: string) {
76+
constructor(basePath: () => string) {
7777
this.basePath = basePath;
7878
}
7979

8080
protected read() {
8181
if (this.content === null) {
82-
this.content = fs.readFileSync(this.basePath, "utf8");
82+
this.content = fs.readFileSync(this.basePath(), "utf8");
8383
}
8484

8585
return this.content;
8686
}
8787

8888
write() {
8989
if (this.content) {
90-
return writeFile(this.basePath, this.content);
90+
return writeFile(this.basePath(), this.content);
9191
}
9292
}
9393
}
@@ -170,18 +170,24 @@ class BuildGradleManager extends BaseFileManager {
170170
}
171171

172172
class PackageJSONManager {
173-
private readonly basePath: string;
174-
private content: {
173+
private readonly basePath: () => string;
174+
public content: {
175175
version: string;
176176
} | null = null;
177177

178-
constructor(basePath: string) {
178+
constructor(basePath: () => string) {
179179
this.basePath = basePath;
180180
}
181181

182182
private read() {
183183
if (this.content === null) {
184-
this.content = require(this.basePath);
184+
// Avoid direct require as it caches
185+
// resolved modules and changes to them
186+
// are going to be persisted in the same process
187+
const raw = fs.readFileSync(require.resolve(this.basePath()), {
188+
encoding: "utf8",
189+
});
190+
this.content = JSON.parse(raw);
185191
}
186192

187193
return this.content!;
@@ -190,7 +196,7 @@ class PackageJSONManager {
190196
write() {
191197
if (this.content) {
192198
return writeFile(
193-
this.basePath,
199+
this.basePath(),
194200
JSON.stringify(this.content, null, 2),
195201
);
196202
}
@@ -208,26 +214,24 @@ class PackageJSONManager {
208214
}
209215
}
210216

211-
export class ProjectFilesManager {
217+
class ProjectFilesManager {
212218
readonly configs: Configs;
213219
readonly pbx: PBXManager;
214220
readonly buildGradle: BuildGradleManager;
215221
readonly packageJSON: PackageJSONManager;
216222

217223
constructor(configs: Configs) {
218-
const {
219-
root,
220-
pbxprojPath,
221-
buildGradlePath,
222-
} = configs;
224+
const { root, pbxprojPath, buildGradlePath } = configs;
223225

224226
this.configs = configs;
225227
this.buildGradle = new BuildGradleManager(buildGradlePath);
226228
this.pbx = new PBXManager(pbxprojPath);
227-
this.packageJSON = new PackageJSONManager(path.join(
228-
root,
229-
"package.json",
230-
));
229+
this.packageJSON = new PackageJSONManager(() =>
230+
path.join(
231+
root,
232+
"package.json",
233+
)
234+
);
231235
}
232236

233237
syncSemver(semverString: string) {
@@ -309,6 +313,82 @@ export class ProjectFilesManager {
309313
}
310314
}
311315

312-
export const versioner = (configs: Configs) => {
313-
new ProjectFilesManager(configs).run();
316+
// if you want a simple version to use as api
317+
export const apiVersioner = (configs: Configs) => {
318+
return new ProjectFilesManager(configs);
319+
};
320+
321+
export const versioner = (
322+
cliConfigs: {
323+
root?: string;
324+
project?: {
325+
ios?: {
326+
sourceDir?: string;
327+
pbxprojPath?: string;
328+
xcodeProject?: {
329+
name: string;
330+
};
331+
};
332+
android?: {
333+
sourceDir?: string;
334+
appName?: string;
335+
};
336+
};
337+
},
338+
cliArgs: {
339+
skipCodeFor?: string;
340+
skipSemverFor?: string;
341+
semver?: string;
342+
type?: string;
343+
},
344+
) => {
345+
if (cliArgs.skipCodeFor === "all" && cliArgs.skipSemverFor === "all") {
346+
// https://i.kym-cdn.com/photos/images/newsfeed/001/240/075/90f.png
347+
console.log("My work here is done.");
348+
return;
349+
}
350+
351+
const required = <T>(value: T, name: string): NonNullable<T> => {
352+
if (!value) {
353+
throw new Error(
354+
`Value for ${name} is '${value}', maybe RN cli broke compatibility?`,
355+
);
356+
}
357+
358+
return value!;
359+
};
360+
361+
return apiVersioner({
362+
root: required(cliConfigs.root, "root"),
363+
pbxprojPath: () => {
364+
const iosProject = required(cliConfigs?.project?.ios, "project.ios");
365+
366+
return iosProject.pbxprojPath || path.join(
367+
required(iosProject.sourceDir, "project.ios.sourceDir"),
368+
required(iosProject.xcodeProject, "project.ios.xcodeProject")
369+
.name
370+
.replace(".xcworkspace", ".xcodeproj"),
371+
"project.pbxproj",
372+
);
373+
},
374+
buildGradlePath: () => {
375+
const androidProject = required(cliConfigs?.project?.android, "project.android");
376+
377+
return path.join(
378+
required(androidProject.sourceDir, "project.android.sourceDir"),
379+
required(androidProject.appName, "project.android.appName"),
380+
"build.gradle",
381+
);
382+
},
383+
// code validates these casts, we cast to make api above allow only valid values
384+
// for type checked usages
385+
type: cliArgs.type as SemVer,
386+
semver: cliArgs.semver,
387+
skipCodeFor: cliArgs.skipCodeFor
388+
? cliArgs.skipCodeFor.split(" ") as Platforms[]
389+
: [],
390+
skipSemVerFor: cliArgs.skipSemverFor
391+
? cliArgs.skipSemverFor.split(" ") as Platforms[]
392+
: [],
393+
});
314394
};

tests/__snapshots__/android.test.js.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ exports[`preserve quotes style 1`] = `
2020
"android {
2121
defaultConfig {
2222
versionCode 2
23-
versionName '1.2.0'
23+
versionName '1.1.0'
2424
}
2525
}
2626
"

tests/__snapshots__/ios.test.js.snap

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,33 @@ exports[`skip semVer when asked 1`] = `
6060
"
6161
`;
6262

63+
exports[`successfully bump version (pre 0.69.x) 1`] = `
64+
"// !$*UTF8*$!
65+
{
66+
objects = {
67+
/* Begin XCBuildConfiguration section */
68+
13B07F941A680F5B01A75B9A /* Debug */ = {
69+
isa = XCBuildConfiguration;
70+
buildSettings = {
71+
MARKETING_VERSION = 1.1.0;
72+
CURRENT_PROJECT_VERSION = 32;
73+
};
74+
name = Debug;
75+
};
76+
13B07F951A680F5B01A75B9A /* Release */ = {
77+
isa = XCBuildConfiguration;
78+
buildSettings = {
79+
MARKETING_VERSION = 1.1.0;
80+
CURRENT_PROJECT_VERSION = 32;
81+
};
82+
name = Release;
83+
};
84+
/* End XCBuildConfiguration section */
85+
};
86+
}
87+
"
88+
`;
89+
6390
exports[`successfully bump version 1`] = `
6491
"// !$*UTF8*$!
6592
{

tests/android.test.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
const path = require("path");
2-
const { ProjectFilesManager } = require("../lib/index");
2+
const { versioner } = require("../lib/index");
33

44
const makeDefaultManager = ({
55
semver,
66
type = "minor",
7-
skipSemVerFor = "ios",
7+
skipSemverFor = "ios",
88
skipCodeFor = "ios",
9-
gradleFileName = "double.gradle",
9+
appName = "double",
1010
} = {}) =>
11-
new ProjectFilesManager({
11+
versioner({
12+
root: path.join(__dirname, "android"),
13+
project: {
14+
android: {
15+
appName: appName,
16+
sourceDir: path.join(__dirname, "android"),
17+
},
18+
},
19+
}, {
1220
type,
1321
semver,
14-
skipSemVerFor,
22+
skipSemverFor,
1523
skipCodeFor,
16-
root: path.join(__dirname, "android"),
17-
buildGradlePath: path.join(__dirname, "android", gradleFileName),
1824
});
1925

2026
test("successfully bump version", () => {
@@ -24,13 +30,13 @@ test("successfully bump version", () => {
2430
});
2531

2632
test("skip semVer when asked", () => {
27-
const manager = makeDefaultManager({ skipSemVerFor: "all" }).dryRun();
33+
const manager = makeDefaultManager({ skipSemverFor: "all" }).dryRun();
2834

2935
expect(manager.buildGradle.content).toMatchSnapshot();
3036
});
3137

3238
test("preserve quotes style", () => {
33-
const manager = makeDefaultManager({ gradleFileName: "single.gradle" }).dryRun();
39+
const manager = makeDefaultManager({ appName: "single" }).dryRun();
3440

3541
expect(manager.buildGradle.content).toMatchSnapshot();
3642
});
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)