Skip to content

Commit

Permalink
refactor(split): change task handling & split stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
renejfc committed Apr 10, 2024
1 parent f3c23c3 commit cb22691
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 89 deletions.
2 changes: 1 addition & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CommitType } from "./types"
import type { CommitType } from "~/types"

export const LINE_MAX_LENGTH = 100
export const LINE_MIN_LENGTH = 3
Expand Down
114 changes: 27 additions & 87 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,100 +1,40 @@
#!/usr/bin/env bun

import { cancel, confirm, group, intro, log, note, outro, select, spinner, text } from "@clack/prompts"
import { intro, log, outro } from "@clack/prompts"
import { $ } from "bun"
import c from "picocolors"
import { COMMIT_TYPE, LINE_MAX_LENGTH, LINE_MIN_LENGTH } from "~/config"
import { getCommitMessage } from "~/utils"
import { commitPrompt } from "~/prompts"
import { getCommitMessage, handleNonZeroExit, tasks } from "~/utils"

console.clear()
$.nothrow()

intro(c.bold(c.bgCyan("COMMIT STRUCTURE")))
intro(c.bold(c.bgCyan(" CONMMIT ")))

const commitResults = await group(
{
type: () =>
select({
initialValue: COMMIT_TYPE[0].name,
// without initialValue ^ ts sets the option.value type to void idk
// see: https://github.com/natemoo-re/clack/issues/178
options: COMMIT_TYPE.map(({ name, description }) => ({
value: name,
label: name,
hint: description,
})),
message: "Choose a commit type",
}),
scope: ({ results }) =>
text({
message: `Enter a scope ${c.italic(c.dim("(optional)"))}`,
placeholder: `Examples: ${COMMIT_TYPE.find(t => t.name === results.type)?.exampleScopes.join(", ")}, etc...`,
}),
subject: () =>
text({
message: "Enter a subject",
placeholder: `Example: "change files structure"`,
validate: value => {
if (!value) return "Subject is required"
if (value.length < LINE_MIN_LENGTH) return `Subject must be at least ${c.bold(c.red(3))} characters`
if (value.length > LINE_MAX_LENGTH)
return `Subject must be less than ${c.bold(c.red(LINE_MAX_LENGTH))} characters`
},
}),
showMessage: ({ results }) =>
note(
c.bold(
c.bgYellow(
getCommitMessage({
type: results.type!,
subject: results.subject!,
scope: results.scope as string,
})
)
),
c.bold(c.italic("Current commit message"))
),
confirmation: () =>
confirm({
message: "Looks good? Wanna commit this?",
active: "Yeah",
inactive: "Nope",
}),
},
const commitResults = await commitPrompt()

await tasks([
{
onCancel: () => {
cancel("Commit cancelled.")
process.exit(0)
progress: ["Committing", "Commit created!"],
enabled: true,
task: async ({ stop }) => {
const { stderr, stdout, exitCode } = await $`git commit -m ${getCommitMessage({
type: commitResults.type,
subject: commitResults.subject,
scope: commitResults.scope as string,
})}`.quiet()

const [error, output] = [stderr.toString(), stdout.toString()]

handleNonZeroExit(() => stop(c.bold("Commit failed."), exitCode), {
error,
output,
exitCode,
})

return () => log.info(c.italic(output))
},
}
)

if (!commitResults.confirmation) {
cancel("Commit cancelled.")
process.exit(0)
}

const s = spinner()
s.start("Committing")

const commit = await $`git commit -m ${getCommitMessage({
type: commitResults.type,
subject: commitResults.subject,
scope: commitResults.scope as string,
})}`.quiet()

const [error, output] = [commit.stderr.toString(), commit.stdout.toString()]

if (commit.exitCode !== 0) {
s.stop(`${c.bold("Commit failed.")}`, commit.exitCode)

if (error) log.error(c.italic(error))
else log.info(c.italic(output))

outro("Try again.")
process.exit(0)
}
},
])

s.stop(c.bold("Commit created!"), 0)
log.info(c.italic(output))
outro("glhf! :)")
65 changes: 65 additions & 0 deletions src/prompts/commit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { confirm, group, note, select, text } from "@clack/prompts"
import c from "picocolors"
import { COMMIT_TYPE, LINE_MAX_LENGTH, LINE_MIN_LENGTH } from "~/config"
import { cancelOnCancel, getCommitMessage } from "~/utils"

export const commitPrompt = async () => {
const results = await group(
{
type: () =>
select({
initialValue: COMMIT_TYPE[0].name,
// without initialValue ^ ts sets the option.value type to void idk
// see: https://github.com/natemoo-re/clack/issues/178
options: COMMIT_TYPE.map(({ name, description }) => ({
value: name,
label: name,
hint: description,
})),
message: "Choose a commit type",
}),
scope: ({ results }) =>
text({
message: `Type a scope ${c.italic(c.dim("(optional)"))}`,
placeholder: `Examples: ${COMMIT_TYPE.find(t => t.name === results.type)?.exampleScopes.join(", ")}, etc...`,
}),
subject: () =>
text({
message: "Whats the subject?",
placeholder: `Example: "change files structure"`,
validate: value => {
if (!value) return "Subject is required"
if (value.length < LINE_MIN_LENGTH) return `Subject must be at least ${c.bold(c.red(3))} characters`
if (value.length > LINE_MAX_LENGTH)
return `Subject must be less than ${c.bold(c.red(LINE_MAX_LENGTH))} characters`
},
}),
showMessage: ({ results }) =>
note(
c.bold(
c.bgYellow(
getCommitMessage({
type: results.type!,
subject: results.subject!,
scope: results.scope as string,
})
)
),
c.bold(c.italic("Current commit message"))
),
confirmation: () =>
confirm({
message: "Looks good? Wanna commit this?",
active: "Yeah",
inactive: "Nope",
}),
},
{
onCancel: () => cancelOnCancel(),
}
)

if (!results.confirmation) cancelOnCancel()

return results
}
1 change: 1 addition & 0 deletions src/prompts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./commit"
11 changes: 11 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ export type CommitType = {
description: string
exampleScopes: string[]
}

export type Task = {
progress: [string, string?]
task: ({
message,
stop,
}: { message: (string: string) => void; stop: (msg: string, code?: number) => void }) =>
| Promise<() => void>
| Promise<void>
enabled?: boolean
}
48 changes: 47 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,50 @@
export function getCommitMessage({ type, subject, scope }: { type: string; subject: string; scope?: string }) {
import { cancel, isCancel, log, outro, spinner } from "@clack/prompts"
import c from "picocolors"
import type { Task } from "~/types"

export const getCommitMessage = ({ type, subject, scope }: { type: string; subject: string; scope?: string }) => {
const scopeStr = scope ? `(${scope})` : ""
return `${type}${scopeStr}: ${subject}`
}

export const cancelOnCancel = (value?: unknown) => {
const cb = () => {
cancel("Commit cancelled.")
process.exit(0)
}

if (value) {
if (isCancel(value)) cb()
return
}

cb()
}

export const tasks = async (tasks: Task[]) => {
for (const { progress, task, enabled } of tasks) {
if (!enabled) continue

const { start, message, stop } = spinner()
const [title, success] = progress

start(c.bold(title))
const taskCb = await task({ message, stop })
stop(c.bold(success || title))
taskCb?.()
}
}

export const handleNonZeroExit = (
callback: () => void,
{ exitCode, error, output }: { exitCode: number; error: string; output: string }
) => {
if (exitCode === 0) return

callback()
if (error) log.error(c.italic(error))
else log.info(c.italic(output))

outro("Try again.")
process.exit(exitCode)
}

0 comments on commit cb22691

Please sign in to comment.