Skip to content

Commit

Permalink
feat(git): add git package (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
tugrulates authored Feb 13, 2025
1 parent cac87f1 commit dc063b9
Show file tree
Hide file tree
Showing 7 changed files with 1,822 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/workflows/title.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ jobs:
http
git
github
git
testing
43 changes: 43 additions & 0 deletions core/git/conventional.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { Commit } from "@roka/git";
import { assert } from "@std/assert";

/**
* A commit object that exposes conventional commit details.
*
* For example, a commit summary like `feat(cli): add new command` will have
* its type set to `feat` and modules set to `cli`.
*
* A {@linkcode ConventionalCommit} object can be converted to a
* {@linkcode ConventionalCommit} using the {@linkcode conventional} function.
*
* @see {@link https://www.conventionalcommits.org|Conventional Commits}
*/
export interface ConventionalCommit extends Commit {
/** Conventional commits: Commit description. */
description: string;
/** Conventional commits: Commit type. */
type: string | undefined;
/** Conventional commits: Modules affected by the commit. */
modules: string[];
/** Conventional commits: Whether the commit is a breaking change. */
breaking: boolean;
}

const SUMMARY_PATTERN =
/^(?:(?<type>[a-z]+)(?:\((?<modules>[^()]*)\))?(?<breaking>!?):s*)?\s*(?<description>[^\s].*)$/;

/** Creates a commit object with conventional commit details. */
export function conventional(commit: Commit): ConventionalCommit {
const match = commit.summary?.match(SUMMARY_PATTERN);
const footerBreaking = commit.body?.match(
/(^|\n\n)BREAKING CHANGE: (.+)($|\n)/,
);
assert(match?.groups?.description, "Commit must have description");
return {
...commit,
description: match.groups.description,
type: match.groups.type,
modules: match.groups.modules?.split(",").map((m) => m.trim()) ?? [],
breaking: !!footerBreaking || !!match.groups.breaking,
};
}
94 changes: 94 additions & 0 deletions core/git/conventional_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type { Commit } from "@roka/git";
import { conventional } from "@roka/git/conventional";
import { assertEquals } from "@std/assert";

function testCommit(summary: string): Commit {
return {
hash: "hash",
short: "short",
author: { name: "author-name", email: "author-email" },
committer: { name: "committer-name", email: "committer-email" },
summary: summary,
body: "body",
};
}

Deno.test("conventional() creates conventional commits", () => {
const commit = testCommit("feat(module): description");
assertEquals(conventional(commit), {
...commit,
description: "description",
type: "feat",
modules: ["module"],
breaking: false,
});
});

Deno.test("conventional() accepts be simple commits", () => {
const commit = testCommit("description");
assertEquals(conventional(commit), {
...commit,
description: "description",
type: undefined,
modules: [],
breaking: false,
});
});

Deno.test("conventional() can create breaking commits", () => {
const commit = testCommit("feat!: description");
assertEquals(conventional(commit), {
...commit,
description: "description",
type: "feat",
modules: [],
breaking: true,
});
});

Deno.test("conventional() can create breaking commits from footer", () => {
const commit = {
...testCommit("feat: description"),
body: "BREAKING CHANGE: breaking",
};
assertEquals(conventional(commit), {
...commit,
description: "description",
type: "feat",
modules: [],
breaking: true,
});
});

Deno.test("conventional() can create breaking commit with module", () => {
const commit = testCommit("feat(module)!: description");
assertEquals(conventional(commit), {
...commit,
description: "description",
type: "feat",
modules: ["module"],
breaking: true,
});
});

Deno.test("conventional() can create multiple modules", () => {
const commit = testCommit("feat(module1,module2): description");
assertEquals(conventional(commit), {
...commit,
description: "description",
type: "feat",
modules: ["module1", "module2"],
breaking: false,
});
});

Deno.test("conventional() commits must have a description", () => {
const commit = testCommit("feat(module): ");
assertEquals(conventional(commit), {
...commit,
description: "feat(module): ",
type: undefined,
modules: [],
breaking: false,
});
});
7 changes: 7 additions & 0 deletions core/git/deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@roka/git",
"exports": {
".": "./git.ts",
"./conventional": "./conventional.ts"
}
}
Loading

0 comments on commit dc063b9

Please sign in to comment.