@ci-kit/changelog 0.0.0-stage → 0.0.1

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/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+
package/dist/cli.mjs ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { writeFileSync } from 'node:fs';
3
+ import process from 'node:process';
4
+ import { cac } from 'cac';
5
+ import { collectCommits, renderChangelog } from './index.mjs';
6
+ import 'node:child_process';
7
+
8
+ const version = "0.0.1";
9
+ const pkg = {
10
+ version: version};
11
+
12
+ const cli = cac("ci-kit-changelog");
13
+ cli.command("[out]", "Generate grouped release notes from git history").option("--from <tag>", "Previous tag (exclusive)").option("--to <tag>", "Target tag (inclusive, defaults to HEAD)").option("--max <n>", "Max items per section", { default: 10 }).action((out, options) => {
14
+ const commits = collectCommits({ from: options.from, to: options.to });
15
+ const markdown = renderChangelog(commits, {
16
+ maxPerSection: Number(options.max ?? 10)
17
+ });
18
+ if (out) {
19
+ writeFileSync(out, `${markdown}
20
+ `, "utf8");
21
+ process.stdout.write(`Wrote ${out} (${commits.length} commits scanned)
22
+ `);
23
+ } else {
24
+ process.stdout.write(`${markdown}
25
+ `);
26
+ }
27
+ });
28
+ cli.help();
29
+ cli.version(pkg.version);
30
+ cli.parse();
@@ -0,0 +1,40 @@
1
+ interface Commit {
2
+ hash: string;
3
+ subject: string;
4
+ body: string;
5
+ }
6
+ interface ChangelogSection {
7
+ type: string;
8
+ title: string;
9
+ items: string[];
10
+ }
11
+ interface ChangelogOptions {
12
+ cwd?: string;
13
+ from?: string;
14
+ to?: string;
15
+ maxPerSection?: number;
16
+ keep?: Array<{
17
+ type: string;
18
+ title: string;
19
+ }>;
20
+ drop?: string[];
21
+ }
22
+ declare const DEFAULT_KEEP: {
23
+ type: string;
24
+ title: string;
25
+ }[];
26
+ declare const DEFAULT_DROP: string[];
27
+ declare function collectCommits(options?: ChangelogOptions): Commit[];
28
+ interface ParsedSubject {
29
+ type: string;
30
+ description: string;
31
+ breaking: boolean;
32
+ references: string;
33
+ }
34
+ declare function parseSubject(subject: string): ParsedSubject | null;
35
+ declare function renderChangelog(commits: Commit[], options?: ChangelogOptions): string;
36
+ declare function isBetaTag(tag: string): boolean;
37
+ declare function previousTagMatcher(channel: 'beta' | 'production'): string;
38
+
39
+ export { DEFAULT_DROP, DEFAULT_KEEP, collectCommits, isBetaTag, parseSubject, previousTagMatcher, renderChangelog };
40
+ export type { ChangelogOptions, ChangelogSection, Commit };
@@ -0,0 +1,40 @@
1
+ interface Commit {
2
+ hash: string;
3
+ subject: string;
4
+ body: string;
5
+ }
6
+ interface ChangelogSection {
7
+ type: string;
8
+ title: string;
9
+ items: string[];
10
+ }
11
+ interface ChangelogOptions {
12
+ cwd?: string;
13
+ from?: string;
14
+ to?: string;
15
+ maxPerSection?: number;
16
+ keep?: Array<{
17
+ type: string;
18
+ title: string;
19
+ }>;
20
+ drop?: string[];
21
+ }
22
+ declare const DEFAULT_KEEP: {
23
+ type: string;
24
+ title: string;
25
+ }[];
26
+ declare const DEFAULT_DROP: string[];
27
+ declare function collectCommits(options?: ChangelogOptions): Commit[];
28
+ interface ParsedSubject {
29
+ type: string;
30
+ description: string;
31
+ breaking: boolean;
32
+ references: string;
33
+ }
34
+ declare function parseSubject(subject: string): ParsedSubject | null;
35
+ declare function renderChangelog(commits: Commit[], options?: ChangelogOptions): string;
36
+ declare function isBetaTag(tag: string): boolean;
37
+ declare function previousTagMatcher(channel: 'beta' | 'production'): string;
38
+
39
+ export { DEFAULT_DROP, DEFAULT_KEEP, collectCommits, isBetaTag, parseSubject, previousTagMatcher, renderChangelog };
40
+ export type { ChangelogOptions, ChangelogSection, Commit };
package/dist/index.mjs ADDED
@@ -0,0 +1,112 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import process from 'node:process';
3
+
4
+ const HEADER = "## \u66F4\u65B0\u5185\u5BB9";
5
+ const EMPTY_NOTICE = "\u672C\u6B21\u7248\u672C\u4E3B\u8981\u4E3A\u5185\u90E8\u7EF4\u62A4\u4E0E\u7A33\u5B9A\u6027\u8C03\u6574\uFF0C\u6CA1\u6709\u9762\u5411\u7528\u6237\u7684\u529F\u80FD\u53D8\u66F4\u3002";
6
+ const FIRST_RELEASE_NOTICE = "\u9996\u4E2A\u7248\u672C\u3002";
7
+ const DEFAULT_KEEP = [
8
+ { type: "feat", title: "\u65B0\u529F\u80FD" },
9
+ { type: "fix", title: "\u4FEE\u590D" },
10
+ { type: "perf", title: "\u6027\u80FD" },
11
+ { type: "refactor", title: "\u91CD\u6784" }
12
+ ];
13
+ const DEFAULT_DROP = [
14
+ "ci",
15
+ "docs",
16
+ "chore",
17
+ "test",
18
+ "build",
19
+ "style"
20
+ ];
21
+ function collectCommits(options = {}) {
22
+ const { cwd = process.cwd(), from, to } = options;
23
+ const range = from ? `${from}..${to ?? "HEAD"}` : to ?? "HEAD";
24
+ const output = execFileSync(
25
+ "git",
26
+ [
27
+ "log",
28
+ range,
29
+ "--no-merges",
30
+ "--pretty=format:%H%x1f%s%x1f%b%x1e"
31
+ ],
32
+ { cwd, encoding: "utf8", maxBuffer: 16 * 1024 * 1024 }
33
+ );
34
+ return output.split("").map((chunk) => chunk.trim()).filter(Boolean).map((chunk) => {
35
+ const [hash = "", subject = "", body = ""] = chunk.split("");
36
+ return {
37
+ hash,
38
+ subject: subject.trim(),
39
+ body: body.trim()
40
+ };
41
+ });
42
+ }
43
+ const PREFIX_RE = /^(feat|fix|perf|refactor|ci|docs|chore|test|build|style)(?:\([^)]+\))?(!)?:\s+/;
44
+ function parseSubject(subject) {
45
+ const match = subject.match(PREFIX_RE);
46
+ if (!match) {
47
+ return null;
48
+ }
49
+ const [, type, breaking] = match;
50
+ const description = subject.slice(match[0].length).trim();
51
+ const references = description.match(/#\d+/g)?.join(" ") ?? "";
52
+ return {
53
+ type,
54
+ description: description.trim(),
55
+ breaking: breaking === "!",
56
+ references
57
+ };
58
+ }
59
+ function renderChangelog(commits, options = {}) {
60
+ const {
61
+ keep = DEFAULT_KEEP,
62
+ drop = DEFAULT_DROP,
63
+ maxPerSection = 10
64
+ } = options;
65
+ if (commits.length === 0) {
66
+ return `${HEADER}
67
+
68
+ ${FIRST_RELEASE_NOTICE}`;
69
+ }
70
+ const sections = /* @__PURE__ */ new Map();
71
+ for (const commit of commits) {
72
+ const parsed = parseSubject(commit.subject);
73
+ if (!parsed || drop.includes(parsed.type)) {
74
+ continue;
75
+ }
76
+ if (!keep.some((entry) => entry.type === parsed.type)) {
77
+ continue;
78
+ }
79
+ const items = sections.get(parsed.type) ?? [];
80
+ items.push(parsed.breaking ? `${parsed.description}\uFF08\u4E0D\u517C\u5BB9\uFF09` : parsed.description);
81
+ sections.set(parsed.type, items);
82
+ }
83
+ const lines = [HEADER, ""];
84
+ let rendered = false;
85
+ for (const { type, title } of keep) {
86
+ const items = sections.get(type);
87
+ if (!items || items.length === 0) {
88
+ continue;
89
+ }
90
+ rendered = true;
91
+ lines.push(`### ${title}`, "");
92
+ for (const item of items.slice(0, maxPerSection)) {
93
+ lines.push(`- ${item}`);
94
+ }
95
+ if (items.length > maxPerSection) {
96
+ lines.push(`- \u4EE5\u53CA\u53E6\u5916 ${items.length - maxPerSection} \u6761`);
97
+ }
98
+ lines.push("");
99
+ }
100
+ if (!rendered) {
101
+ lines.push(EMPTY_NOTICE, "");
102
+ }
103
+ return lines.join("\n").trimEnd();
104
+ }
105
+ function isBetaTag(tag) {
106
+ return /\d+\.\d+\.\d+-beta\.\d+$/.test(tag);
107
+ }
108
+ function previousTagMatcher(channel) {
109
+ return channel === "beta" ? "v*-beta.*" : "v[0-9]*";
110
+ }
111
+
112
+ export { DEFAULT_DROP, DEFAULT_KEEP, collectCommits, isBetaTag, parseSubject, previousTagMatcher, renderChangelog };
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "name": "@ci-kit/changelog",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "description": "Turn git commits into grouped release notes",
6
+ "author": "imba97",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/cikit-js/ci-kit#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/cikit-js/ci-kit.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/cikit-js/ci-kit/issues"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.mts",
19
+ "import": "./dist/index.mjs"
20
+ }
21
+ },
22
+ "main": "./dist/index.mjs",
23
+ "module": "./dist/index.mjs",
24
+ "types": "./dist/index.d.mts",
25
+ "bin": {
26
+ "ci-kit-changelog": "dist/cli.mjs"
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "dependencies": {
32
+ "cac": "^7.1.0"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "scripts": {
38
+ "build": "unbuild",
39
+ "typecheck": "tsc --noEmit",
40
+ "stub": "unbuild --stub"
41
+ }
6
42
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.