gflows 0.1.12 → 0.1.14

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/src/config.ts CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
8
8
  import { join } from "node:path";
9
+ import { DEFAULT_DEV, DEFAULT_MAIN, DEFAULT_PREFIXES, DEFAULT_REMOTE } from "./constants.js";
9
10
  import type {
10
11
  BranchPrefixes,
11
12
  BranchType,
@@ -13,12 +14,6 @@ import type {
13
14
  GflowsConfigFile,
14
15
  ResolvedConfig,
15
16
  } from "./types.js";
16
- import {
17
- DEFAULT_DEV,
18
- DEFAULT_MAIN,
19
- DEFAULT_PREFIXES,
20
- DEFAULT_REMOTE,
21
- } from "./constants.js";
22
17
 
23
18
  const CONFIG_FILE = ".gflows.json";
24
19
  const PACKAGE_JSON = "package.json";
@@ -99,10 +94,22 @@ function normalizeConfigFile(data: unknown): GflowsConfigFile | null {
99
94
  if (typeof obj.remote === "string" && obj.remote.trim() !== "") {
100
95
  out.remote = obj.remote.trim();
101
96
  }
102
- if (obj.prefixes !== undefined && obj.prefixes !== null && typeof obj.prefixes === "object" && !Array.isArray(obj.prefixes)) {
97
+ if (
98
+ obj.prefixes !== undefined &&
99
+ obj.prefixes !== null &&
100
+ typeof obj.prefixes === "object" &&
101
+ !Array.isArray(obj.prefixes)
102
+ ) {
103
103
  const prefs = obj.prefixes as Record<string, unknown>;
104
104
  const prefixes: BranchPrefixes = {};
105
- const keys: (keyof BranchPrefixes)[] = ["feature", "bugfix", "chore", "release", "hotfix", "spike"];
105
+ const keys: (keyof BranchPrefixes)[] = [
106
+ "feature",
107
+ "bugfix",
108
+ "chore",
109
+ "release",
110
+ "hotfix",
111
+ "spike",
112
+ ];
106
113
  for (const k of keys) {
107
114
  const v = prefs[k];
108
115
  if (typeof v === "string" && v.trim() !== "") {
@@ -123,10 +130,17 @@ function normalizeConfigFile(data: unknown): GflowsConfigFile | null {
123
130
  function mergePrefixes(overrides?: BranchPrefixes): Required<BranchPrefixes> {
124
131
  const result: Required<BranchPrefixes> = { ...DEFAULT_PREFIXES };
125
132
  if (!overrides) return result;
126
- const keys: (keyof BranchPrefixes)[] = ["feature", "bugfix", "chore", "release", "hotfix", "spike"];
133
+ const keys: (keyof BranchPrefixes)[] = [
134
+ "feature",
135
+ "bugfix",
136
+ "chore",
137
+ "release",
138
+ "hotfix",
139
+ "spike",
140
+ ];
127
141
  for (const k of keys) {
128
- if (typeof overrides[k] === "string" && overrides[k]!.trim() !== "") {
129
- result[k] = overrides[k]!.trim();
142
+ if (typeof overrides[k] === "string" && overrides[k]?.trim() !== "") {
143
+ result[k] = overrides[k]?.trim();
130
144
  }
131
145
  }
132
146
  return result;
@@ -144,7 +158,7 @@ function mergePrefixes(overrides?: BranchPrefixes): Required<BranchPrefixes> {
144
158
  export function resolveConfig(
145
159
  dir: string,
146
160
  cliOverrides?: ConfigCliOverrides,
147
- options?: ResolveConfigOptions
161
+ options?: ResolveConfigOptions,
148
162
  ): ResolvedConfig {
149
163
  const verbose = options?.verbose === true;
150
164
 
@@ -180,10 +194,7 @@ export function resolveConfig(
180
194
  * @param dir - Repo root directory.
181
195
  * @param partial - Keys to set (main, dev, remote, prefixes); omitted keys are left unchanged.
182
196
  */
183
- export function writeConfigFile(
184
- dir: string,
185
- partial: Partial<GflowsConfigFile>
186
- ): void {
197
+ export function writeConfigFile(dir: string, partial: Partial<GflowsConfigFile>): void {
187
198
  const path = join(dir, CONFIG_FILE);
188
199
  let existing: GflowsConfigFile = {};
189
200
  if (existsSync(path)) {
@@ -206,10 +217,22 @@ export function writeConfigFile(
206
217
  if (typeof partial.remote === "string" && partial.remote.trim() !== "") {
207
218
  merged.remote = partial.remote.trim();
208
219
  }
209
- if (partial.prefixes !== undefined && partial.prefixes !== null && typeof partial.prefixes === "object" && !Array.isArray(partial.prefixes)) {
220
+ if (
221
+ partial.prefixes !== undefined &&
222
+ partial.prefixes !== null &&
223
+ typeof partial.prefixes === "object" &&
224
+ !Array.isArray(partial.prefixes)
225
+ ) {
210
226
  const prefs = partial.prefixes as Record<string, unknown>;
211
227
  const prefixes: BranchPrefixes = { ...(merged.prefixes ?? {}) };
212
- const keys: (keyof BranchPrefixes)[] = ["feature", "bugfix", "chore", "release", "hotfix", "spike"];
228
+ const keys: (keyof BranchPrefixes)[] = [
229
+ "feature",
230
+ "bugfix",
231
+ "chore",
232
+ "release",
233
+ "hotfix",
234
+ "spike",
235
+ ];
213
236
  for (const k of keys) {
214
237
  const v = prefs[k];
215
238
  if (typeof v === "string" && v.trim() !== "") {
@@ -218,16 +241,13 @@ export function writeConfigFile(
218
241
  }
219
242
  merged.prefixes = prefixes;
220
243
  }
221
- writeFileSync(path, JSON.stringify(merged, null, 2) + "\n", "utf-8");
244
+ writeFileSync(path, `${JSON.stringify(merged, null, 2)}\n`, "utf-8");
222
245
  }
223
246
 
224
247
  /**
225
248
  * Returns the branch name prefix for a given branch type from resolved config.
226
249
  */
227
- export function getPrefixForType(
228
- config: ResolvedConfig,
229
- type: BranchType
230
- ): string {
250
+ export function getPrefixForType(config: ResolvedConfig, type: BranchType): string {
231
251
  return config.prefixes[type] ?? DEFAULT_PREFIXES[type];
232
252
  }
233
253
 
package/src/constants.ts CHANGED
@@ -35,4 +35,4 @@ export const DEFAULT_PREFIXES = {
35
35
  export const VERSION_REGEX = /^v?\d+\.\d+\.\d+$/;
36
36
 
37
37
  /** Characters invalid in Git ref names (branch names). */
38
- export const INVALID_BRANCH_CHARS = /\.\.|[\s~^?:*\[\]\\]/;
38
+ export const INVALID_BRANCH_CHARS = /\.\.|[\s~^?:*[\]\\]/;
package/src/errors.ts CHANGED
@@ -36,7 +36,9 @@ export class BranchNotFoundError extends GflowsError {
36
36
 
37
37
  /** Thrown when start is run with uncommitted changes and without --force. */
38
38
  export class DirtyWorkingTreeError extends GflowsError {
39
- constructor(message = "Working tree has uncommitted changes. Commit or stash them, or use --force.") {
39
+ constructor(
40
+ message = "Working tree has uncommitted changes. Commit or stash them, or use --force.",
41
+ ) {
40
42
  super(message, EXIT_GIT);
41
43
  }
42
44
  }
@@ -51,7 +53,7 @@ export class DetachedHeadError extends GflowsError {
51
53
  /** Thrown when a rebase or merge is in progress; user must complete or abort first. */
52
54
  export class RebaseMergeInProgressError extends GflowsError {
53
55
  constructor(
54
- message = "A rebase or merge is in progress. Complete or abort it before running this command."
56
+ message = "A rebase or merge is in progress. Complete or abort it before running this command.",
55
57
  ) {
56
58
  super(message, EXIT_GIT);
57
59
  }
package/src/git.ts CHANGED
@@ -45,10 +45,7 @@ export interface GitRunResult {
45
45
  * @param options - cwd, dryRun, verbose.
46
46
  * @returns Promise with stdout, stderr, and exitCode.
47
47
  */
48
- export async function runGit(
49
- args: string[],
50
- options: GitOptions
51
- ): Promise<GitRunResult> {
48
+ export async function runGit(args: string[], options: GitOptions): Promise<GitRunResult> {
52
49
  const { cwd, dryRun = false, verbose = false } = options;
53
50
  const cmd = ["git", ...args].join(" ");
54
51
 
@@ -133,7 +130,7 @@ export async function revParse(
133
130
  cwd: string,
134
131
  ref: string,
135
132
  extraArgs: string[] = [],
136
- options: Pick<GitOptions, "dryRun" | "verbose"> = {}
133
+ options: Pick<GitOptions, "dryRun" | "verbose"> = {},
137
134
  ): Promise<string> {
138
135
  const result = await runGit(["rev-parse", ...extraArgs, ref], { cwd, ...options });
139
136
  if (result.exitCode !== 0) {
@@ -151,7 +148,7 @@ export async function revParse(
151
148
  */
152
149
  export async function branchList(
153
150
  cwd: string,
154
- options: GitRunOptions & { includeRemote?: boolean } = {}
151
+ options: GitRunOptions & { includeRemote?: boolean } = {},
155
152
  ): Promise<string[]> {
156
153
  const { includeRemote = false, ...opts } = options;
157
154
  const args = includeRemote
@@ -182,7 +179,7 @@ export async function branchList(
182
179
  export async function checkout(
183
180
  cwd: string,
184
181
  branch: string,
185
- options: GitRunOptions = {}
182
+ options: GitRunOptions = {},
186
183
  ): Promise<void> {
187
184
  const result = await runGit(["checkout", branch], { cwd, ...options });
188
185
  if (result.exitCode !== 0) {
@@ -201,7 +198,7 @@ export async function checkout(
201
198
  export async function merge(
202
199
  cwd: string,
203
200
  ref: string,
204
- options: GitRunOptions & { noFf?: boolean } = {}
201
+ options: GitRunOptions & { noFf?: boolean } = {},
205
202
  ): Promise<void> {
206
203
  const { noFf = false, ...opts } = options;
207
204
  const args = noFf ? ["merge", "--no-ff", ref] : ["merge", ref];
@@ -229,7 +226,7 @@ export async function push(
229
226
  remote: string,
230
227
  refs: string[],
231
228
  pushTags: boolean,
232
- options: GitRunOptions = {}
229
+ options: GitRunOptions = {},
233
230
  ): Promise<number> {
234
231
  const pushArgs = ["push", remote, ...refs];
235
232
  const result = await runGit(pushArgs, { cwd, ...options });
@@ -254,7 +251,7 @@ export async function push(
254
251
  export async function tag(
255
252
  cwd: string,
256
253
  name: string,
257
- options: GitRunOptions & { sign?: boolean; tagMessage?: string } = {}
254
+ options: GitRunOptions & { sign?: boolean; tagMessage?: string } = {},
258
255
  ): Promise<void> {
259
256
  const { sign = false, tagMessage, ...opts } = options;
260
257
  const args = ["tag", name];
@@ -280,7 +277,7 @@ export async function tag(
280
277
  export async function tagExists(
281
278
  cwd: string,
282
279
  name: string,
283
- options: GitRunOptions = {}
280
+ options: GitRunOptions = {},
284
281
  ): Promise<boolean> {
285
282
  const result = await runGit(["tag", "-l", name], { cwd, ...options });
286
283
  return result.exitCode === 0 && result.stdout.trim() === name;
@@ -297,13 +294,11 @@ export async function tagExists(
297
294
  export async function deleteBranch(
298
295
  cwd: string,
299
296
  branch: string,
300
- options: GitRunOptions = {}
297
+ options: GitRunOptions = {},
301
298
  ): Promise<void> {
302
299
  const result = await runGit(["branch", "-d", branch], { cwd, ...options });
303
300
  if (result.exitCode !== 0) {
304
- throw new BranchNotFoundError(
305
- result.stderr.trim() || `Could not delete branch '${branch}'.`
306
- );
301
+ throw new BranchNotFoundError(result.stderr.trim() || `Could not delete branch '${branch}'.`);
307
302
  }
308
303
  }
309
304
 
@@ -313,10 +308,7 @@ export async function deleteBranch(
313
308
  * @param cwd - Repo root.
314
309
  * @param options - dryRun, verbose (verbose has no effect for this read-only call).
315
310
  */
316
- export async function isClean(
317
- cwd: string,
318
- options: GitRunOptions = {}
319
- ): Promise<boolean> {
311
+ export async function isClean(cwd: string, options: GitRunOptions = {}): Promise<boolean> {
320
312
  const result = await runGit(["status", "--porcelain"], { cwd, ...options });
321
313
  if (result.exitCode !== 0) return false;
322
314
  return result.stdout.trim() === "";
@@ -330,7 +322,7 @@ export async function isClean(
330
322
  */
331
323
  export async function getCurrentBranch(
332
324
  cwd: string,
333
- options: GitRunOptions = {}
325
+ options: GitRunOptions = {},
334
326
  ): Promise<string | null> {
335
327
  const result = await runGit(["rev-parse", "--abbrev-ref", "HEAD"], {
336
328
  cwd,
@@ -404,7 +396,7 @@ export function validateBranchName(name: string): void {
404
396
  }
405
397
  if (INVALID_BRANCH_CHARS.test(name)) {
406
398
  throw new InvalidBranchNameError(
407
- "Branch name contains invalid characters (e.g. .., ~, ^, ?, *, [, ], :, \\, space)."
399
+ "Branch name contains invalid characters (e.g. .., ~, ^, ?, *, [, ], :, \\, space).",
408
400
  );
409
401
  }
410
402
  }
@@ -420,7 +412,7 @@ export function validateBranchName(name: string): void {
420
412
  export async function fetch(
421
413
  cwd: string,
422
414
  remote: string,
423
- options: GitRunOptions = {}
415
+ options: GitRunOptions = {},
424
416
  ): Promise<number> {
425
417
  const result = await runGit(["fetch", remote], { cwd, ...options });
426
418
  return result.exitCode;
@@ -442,7 +434,7 @@ export async function hasRemoteRef(
442
434
  cwd: string,
443
435
  remote: string,
444
436
  ref: string,
445
- options: GitRunOptions = {}
437
+ options: GitRunOptions = {},
446
438
  ): Promise<boolean> {
447
439
  const result = await runGit(["ls-remote", "--exit-code", remote, ref], {
448
440
  cwd,
@@ -465,12 +457,12 @@ export async function getAheadBehind(
465
457
  cwd: string,
466
458
  baseRef: string,
467
459
  headRef: string,
468
- options: GitRunOptions = {}
460
+ options: GitRunOptions = {},
469
461
  ): Promise<{ ahead: number; behind: number }> {
470
- const result = await runGit(
471
- ["rev-list", "--left-right", "--count", `${baseRef}...${headRef}`],
472
- { cwd, ...options }
473
- );
462
+ const result = await runGit(["rev-list", "--left-right", "--count", `${baseRef}...${headRef}`], {
463
+ cwd,
464
+ ...options,
465
+ });
474
466
  if (result.exitCode !== 0 || !result.stdout.trim()) {
475
467
  return { ahead: 0, behind: 0 };
476
468
  }
package/src/index.ts CHANGED
@@ -5,80 +5,76 @@
5
5
  */
6
6
 
7
7
  export type {
8
- BranchType,
9
- BranchTypeBase,
10
- BranchPrefixes,
11
- BumpDirection,
12
- BumpType,
13
- Command,
14
- GflowsConfigFile,
15
- MergeTarget,
16
- ParsedArgs,
17
- ResolvedConfig,
18
- BranchTypeMeta,
19
- } from "./types.js";
20
- export { BRANCH_TYPE_SHORTS } from "./types.js";
21
-
8
+ ConfigCliOverrides,
9
+ ReadConfigResult,
10
+ ResolveConfigOptions,
11
+ } from "./config.js";
22
12
  export {
13
+ getBranchTypeMeta,
14
+ getPrefixForType,
23
15
  readConfigFile,
24
16
  resolveConfig,
25
17
  writeConfigFile,
26
- getPrefixForType,
27
- getBranchTypeMeta,
28
18
  } from "./config.js";
29
- export type {
30
- ConfigCliOverrides,
31
- ReadConfigResult,
32
- ResolveConfigOptions,
33
- } from "./config.js";
34
-
35
19
  export {
36
- runGit,
37
- resolveRepoRoot,
38
- ensureGitRepo,
39
- revParse,
20
+ DEFAULT_DEV,
21
+ DEFAULT_MAIN,
22
+ DEFAULT_PREFIXES,
23
+ DEFAULT_REMOTE,
24
+ EXIT_GIT,
25
+ EXIT_OK,
26
+ EXIT_USER,
27
+ INVALID_BRANCH_CHARS,
28
+ VERSION_REGEX,
29
+ } from "./constants.js";
30
+ export {
31
+ BranchNotFoundError,
32
+ CannotDeleteMainOrDevError,
33
+ DetachedHeadError,
34
+ DirtyWorkingTreeError,
35
+ exitCodeForError,
36
+ GflowsError,
37
+ InvalidBranchNameError,
38
+ InvalidVersionError,
39
+ MergeConflictError,
40
+ NotRepoError,
41
+ RebaseMergeInProgressError,
42
+ } from "./errors.js";
43
+ export type { GitOptions, GitRunOptions, GitRunResult } from "./git.js";
44
+ export {
45
+ assertNoRebaseOrMerge,
46
+ assertNotDetached,
40
47
  branchList,
41
48
  checkout,
42
- merge,
43
- push,
44
- tag,
45
- tagExists,
46
49
  deleteBranch,
47
- isClean,
50
+ ensureGitRepo,
51
+ fetch,
52
+ getAheadBehind,
48
53
  getCurrentBranch,
54
+ hasRemoteRef,
55
+ isClean,
49
56
  isDetachedHead,
50
57
  isRebaseOrMergeInProgress,
51
- assertNotDetached,
52
- assertNoRebaseOrMerge,
58
+ merge,
59
+ push,
60
+ resolveRepoRoot,
61
+ revParse,
62
+ runGit,
63
+ tag,
64
+ tagExists,
53
65
  validateBranchName,
54
- fetch,
55
- hasRemoteRef,
56
- getAheadBehind,
57
66
  } from "./git.js";
58
- export type { GitOptions, GitRunOptions, GitRunResult } from "./git.js";
59
-
60
- export {
61
- GflowsError,
62
- NotRepoError,
63
- BranchNotFoundError,
64
- DirtyWorkingTreeError,
65
- DetachedHeadError,
66
- RebaseMergeInProgressError,
67
- MergeConflictError,
68
- InvalidVersionError,
69
- InvalidBranchNameError,
70
- CannotDeleteMainOrDevError,
71
- exitCodeForError,
72
- } from "./errors.js";
73
-
74
- export {
75
- EXIT_OK,
76
- EXIT_USER,
77
- EXIT_GIT,
78
- DEFAULT_MAIN,
79
- DEFAULT_DEV,
80
- DEFAULT_REMOTE,
81
- DEFAULT_PREFIXES,
82
- VERSION_REGEX,
83
- INVALID_BRANCH_CHARS,
84
- } from "./constants.js";
67
+ export type {
68
+ BranchPrefixes,
69
+ BranchType,
70
+ BranchTypeBase,
71
+ BranchTypeMeta,
72
+ BumpDirection,
73
+ BumpType,
74
+ Command,
75
+ GflowsConfigFile,
76
+ MergeTarget,
77
+ ParsedArgs,
78
+ ResolvedConfig,
79
+ } from "./types.js";
80
+ export { BRANCH_TYPE_SHORTS } from "./types.js";
package/src/out.ts CHANGED
@@ -66,11 +66,19 @@ export function banner(title: string, lines?: string[]): void {
66
66
  const top = ` ${c(BOX.TL)}${c(BOX.H.repeat(BANNER_INNER_WIDTH))}${c(BOX.TR)}`;
67
67
  const bottom = ` ${c(BOX.BL)}${c(BOX.H.repeat(BANNER_INNER_WIDTH))}${c(BOX.BR)}`;
68
68
  const row = (text: string) =>
69
- " " + c(BOX.V) + " " + text + " ".repeat(Math.max(0, inner - text.length)) + " " + c(BOX.V);
69
+ ` ${c(BOX.V)} ${text}${" ".repeat(Math.max(0, inner - text.length))} ${c(BOX.V)}`;
70
70
  const titleDisplay = color ? `${CYAN}${BOLD}${title}${RESET}` : title;
71
71
  console.log("");
72
72
  console.log(top);
73
- console.log(" " + c(BOX.V) + " " + titleDisplay + " ".repeat(Math.max(0, inner - title.length)) + " " + c(BOX.V));
73
+ console.log(
74
+ " " +
75
+ c(BOX.V) +
76
+ " " +
77
+ titleDisplay +
78
+ " ".repeat(Math.max(0, inner - title.length)) +
79
+ " " +
80
+ c(BOX.V),
81
+ );
74
82
  if (lines?.length) {
75
83
  for (const line of lines) {
76
84
  console.log(line === "" ? row("") : row(line));
package/src/types.ts CHANGED
@@ -4,13 +4,7 @@
4
4
  */
5
5
 
6
6
  /** Supported workflow branch types (core + optional spike). */
7
- export type BranchType =
8
- | "feature"
9
- | "bugfix"
10
- | "chore"
11
- | "release"
12
- | "hotfix"
13
- | "spike";
7
+ export type BranchType = "feature" | "bugfix" | "chore" | "release" | "hotfix" | "spike";
14
8
 
15
9
  /** Short flag for each branch type when used as CLI type selector. */
16
10
  export const BRANCH_TYPE_SHORTS: Record<BranchType, string> = {