Skip to content

Commit 7e0ea6f

Browse files
Centralized all the operation state into an Operation class
1 parent 2fe06b2 commit 7e0ea6f

7 files changed

Lines changed: 222 additions & 104 deletions

File tree

src/get-new-version.ts

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,44 @@
11
import * as inquirer from "inquirer";
22
import * as semver from "semver";
33
import { ReleaseType, SemVer } from "semver"; // tslint:disable-line: no-duplicate-imports
4-
import { BumpRelease, NormalizedOptions } from "./normalize-options";
4+
import { BumpRelease, PromptRelease } from "./normalize-options";
5+
import { Operation } from "./operation";
56
import { isPrerelease, releaseTypes } from "./release-type";
67

7-
type Params = NormalizedOptions & { oldVersion: string };
8-
type VersionAndReleaseType = [string, ReleaseType?];
9-
108
/**
11-
* Returns the new version number, possibly by prompting the user for it.
12-
*
13-
* @returns - A tuple containing the new version number and the release type (if any)
9+
* Determines the new version number, possibly by prompting the user for it.
1410
*/
15-
export async function getNewVersion(params: Params): Promise<VersionAndReleaseType> {
16-
let { release } = params;
11+
export async function getNewVersion(operation: Operation): Promise<Operation> {
12+
let { release } = operation.options;
13+
let { oldVersion } = operation.state;
1714

1815
switch (release.type) {
1916
case "prompt":
20-
return promptForNewVersion(params);
17+
return promptForNewVersion(operation);
2118

2219
case "version":
2320
let newSemVer = new SemVer(release.version, true);
24-
return [newSemVer.version];
21+
return operation.update({
22+
newVersion: newSemVer.version,
23+
});
2524

2625
default:
27-
return [getNextVersion(params), release.type];
26+
return operation.update({
27+
release: release.type,
28+
newVersion: getNextVersion(oldVersion, release),
29+
});
2830
}
2931
}
3032

3133
/**
3234
* Returns the next version number of the specified type.
3335
*/
34-
function getNextVersion({ oldVersion, release }: Params): string {
35-
let bump = release as BumpRelease;
36-
36+
function getNextVersion(oldVersion: string, bump: BumpRelease): string {
3737
let oldSemVer = new SemVer(oldVersion);
38-
let newSemVer = oldSemVer.inc(bump.type as ReleaseType, bump.preid);
38+
let newSemVer = oldSemVer.inc(bump.type, bump.preid);
3939

4040
if (
41-
isPrerelease(release) &&
41+
isPrerelease(bump.type) &&
4242
newSemVer.prerelease.length === 2 &&
4343
newSemVer.prerelease[0] === bump.preid &&
4444
String(newSemVer.prerelease[1]) === "0"
@@ -56,12 +56,11 @@ function getNextVersion({ oldVersion, release }: Params): string {
5656
/**
5757
* Returns the next version number for all release types.
5858
*/
59-
function getNextVersions(params: Params): Record<ReleaseType, string> {
60-
let release = params.release as BumpRelease;
59+
function getNextVersions(oldVersion: string, preid: string): Record<ReleaseType, string> {
6160
let next: Record<string, string> = {};
6261

6362
for (let type of releaseTypes) {
64-
next[type] = getNextVersion({ ...params, release: { ...release, type }});
63+
next[type] = getNextVersion(oldVersion, { type, preid });
6564
}
6665

6766
return next;
@@ -72,10 +71,12 @@ function getNextVersions(params: Params): Record<ReleaseType, string> {
7271
*
7372
* @returns - A tuple containing the new version number and the release type (if any)
7473
*/
75-
async function promptForNewVersion(params: Params): Promise<VersionAndReleaseType> {
76-
let { oldVersion, interface: ui } = params;
77-
let next = getNextVersions(params);
78-
let prompts = inquirer.createPromptModule(ui as inquirer.StreamOptions);
74+
async function promptForNewVersion(operation: Operation): Promise<Operation> {
75+
let { oldVersion, oldVersionSource } = operation.state;
76+
let release = operation.options.release as PromptRelease;
77+
let prompts = inquirer.createPromptModule(operation.options.interface as inquirer.StreamOptions);
78+
79+
let next = getNextVersions(oldVersion, release.preid);
7980

8081
let answers: {
8182
release: ReleaseType | "none" | "custom";
@@ -86,7 +87,7 @@ async function promptForNewVersion(params: Params): Promise<VersionAndReleaseTyp
8687
{
8788
type: "list",
8889
name: "release",
89-
message: `\nThe current version is ${oldVersion}\nHow would you like to bump it?`,
90+
message: `\nThe current version in ${oldVersionSource} is ${oldVersion}\nHow would you like to bump it?`,
9091
default: "patch",
9192
pageSize: 10,
9293
choices: [
@@ -107,7 +108,7 @@ async function promptForNewVersion(params: Params): Promise<VersionAndReleaseTyp
107108
name: "newVersion",
108109
message: "Enter the new version number:",
109110
default: oldVersion,
110-
when: ({ release }) => release === "custom",
111+
when: (previousAnswer) => previousAnswer.release === "custom",
111112
filter: semver.clean,
112113
validate: (newVersion: string) => {
113114
return semver.valid(newVersion) ? true : "That's not a valid version number";
@@ -117,12 +118,15 @@ async function promptForNewVersion(params: Params): Promise<VersionAndReleaseTyp
117118

118119
switch (answers.release) {
119120
case "none":
120-
return [oldVersion];
121+
return operation.update({ newVersion: oldVersion });
121122

122123
case "custom":
123-
return [answers.newVersion!];
124+
return operation.update({ newVersion: answers.newVersion! });
124125

125126
default:
126-
return [next[answers.release], answers.release];
127+
return operation.update({
128+
release: answers.release,
129+
newVersion: next[answers.release],
130+
});
127131
}
128132
}

src/get-old-version.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import * as semver from "semver";
22
import { readJsonFile } from "./fs";
33
import { isManifest } from "./manifest";
4-
import { NormalizedOptions } from "./normalize-options";
4+
import { Operation } from "./operation";
55

66
/**
7-
* Returns the current version number from files such as package.json.
7+
* Finds the current version number from files such as package.json.
88
* An error is thrown if no version number can be found.
99
*/
10-
export async function getOldVersion({ files, cwd }: NormalizedOptions): Promise<string> {
10+
export async function getOldVersion(operation: Operation): Promise<Operation> {
11+
let { cwd, files } = operation.options;
12+
1113
// Check all JSON files in the files list
1214
let filesToCheck = files.filter((file) => file.endsWith(".json"));
1315

@@ -21,7 +23,11 @@ export async function getOldVersion({ files, cwd }: NormalizedOptions): Promise<
2123
let version = await readVersion(file, cwd);
2224

2325
if (version) {
24-
return version;
26+
// We found the current version number!
27+
return operation.update({
28+
oldVersionSource: file,
29+
oldVersion: version,
30+
});
2531
}
2632
}
2733

src/git.ts

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import * as ezSpawn from "ez-spawn";
2-
import { NormalizedOptions } from "./normalize-options";
3-
4-
type Params = NormalizedOptions & { files: string[]; newVersion: string };
2+
import { Operation } from "./operation";
3+
import { ProgressEvent } from "./version-bump-progress";
54

65
/**
76
* Commits the modififed files to Git, if the `commit` option is enabled.
8-
*
9-
* @returns - The commit message, or `false` if nothing was committed
107
*/
11-
export async function gitCommit({ commit, files, newVersion }: Params): Promise<string | false> {
12-
if (!commit) {
13-
return false;
8+
export async function gitCommit(operation: Operation): Promise<Operation> {
9+
if (!operation.options.commit) {
10+
return operation;
1411
}
1512

16-
let { all, noVerify, message } = commit;
13+
let { all, noVerify, message } = operation.options.commit;
14+
let { files, newVersion } = operation.state;
1715
let args = [];
1816

1917
if (all) {
@@ -27,60 +25,63 @@ export async function gitCommit({ commit, files, newVersion }: Params): Promise<
2725
}
2826

2927
// Create the commit message
30-
message = formatVersionString(message, newVersion);
31-
args.push("--message", message);
28+
let commitMessage = formatVersionString(message, newVersion);
29+
args.push("--message", commitMessage);
3230

3331
// Append the file names last, as variadic arguments
3432
if (!all) {
3533
args = args.concat(files);
3634
}
3735

3836
await ezSpawn.async("git", ["commit", ...args]);
39-
return message;
37+
38+
return operation.update({ event: ProgressEvent.GitCommit, commitMessage });
4039
}
4140

4241
/**
4342
* Tags the Git commit, if the `tag` option is enabled.
44-
*
45-
* @returns - The tag name, or `false` if no tag was created
4643
*/
47-
export async function gitTag({ commit, tag, newVersion }: Params): Promise<string | false> {
48-
if (!commit || !tag) {
49-
return false;
44+
export async function gitTag(operation: Operation): Promise<Operation> {
45+
if (!operation.options.tag) {
46+
return operation;
5047
}
5148

49+
let { commit, tag } = operation.options;
50+
let { newVersion } = operation.state;
51+
5252
let args = [
5353
// Create an annotated tag, which is recommended for releases.
5454
// See https://git-scm.com/docs/git-tag
5555
"--annotate",
5656

5757
// Use the same commit message for the tag
58-
"--message", formatVersionString(commit.message, newVersion),
58+
"--message", formatVersionString(commit!.message, newVersion),
5959
];
6060

6161
// Create the Tag name
62-
let name = formatVersionString(tag.name, newVersion);
63-
args.push(name);
62+
let tagName = formatVersionString(tag.name, newVersion);
63+
args.push(tagName);
6464

6565
await ezSpawn.async("git", ["tag", ...args]);
66-
return name;
66+
67+
return operation.update({ event: ProgressEvent.GitTag, tagName });
6768
}
6869

6970
/**
7071
* Pushes the Git commit and tag, if the `push` option is enabled.
7172
*/
72-
export async function gitPush({ push, tag }: NormalizedOptions): Promise<void> {
73-
if (!push) {
74-
return;
73+
export async function gitPush(operation: Operation): Promise<Operation> {
74+
if (operation.options.push) {
75+
// Push the commit
76+
await ezSpawn.async("git", "push");
77+
78+
if (operation.options.tag) {
79+
// Push the tag
80+
await ezSpawn.async("git", ["push", "--tags"]);
81+
}
7582
}
7683

77-
// Push the commit
78-
await ezSpawn.async("git", "push");
79-
80-
if (tag) {
81-
// Push the tag
82-
await ezSpawn.async("git", ["push", "--tags"]);
83-
}
84+
return operation.update({ event: ProgressEvent.GitPush });
8485
}
8586

8687
/**

src/normalize-options.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,25 @@ export interface VersionRelease {
1717
}
1818

1919
/**
20-
* A bump release (prompted or otherwise), relative to the current version number.
20+
* Prompt the user for the release number.
21+
*/
22+
export interface PromptRelease {
23+
type: "prompt";
24+
preid: string;
25+
}
26+
27+
/**
28+
* A bump release, relative to the current version number.
2129
*/
2230
export interface BumpRelease {
23-
type: "prompt" | ReleaseType;
31+
type: ReleaseType;
2432
preid: string;
2533
}
2634

2735
/**
2836
* One of the possible Release types.
2937
*/
30-
export type Release = VersionRelease | BumpRelease;
38+
export type Release = VersionRelease | PromptRelease | BumpRelease;
3139

3240
/**
3341
* Normalized and sanitized options

0 commit comments

Comments
 (0)