githublogen 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Generate changelog for GitHub releases from [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), powered by [changelogithub](https://github.com/antfu/changelogithub).
4
4
 
5
+ Auto Generate CHANGELOG.md from [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), powered by [changelogen](https://github.com/unjs/changelogen).
6
+
5
7
  ## Usage
6
8
 
7
9
  In GitHub Actions:
package/dist/cli.cjs CHANGED
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- const fs = require('node:fs');
5
4
  const kolorist = require('kolorist');
6
- const execa = require('execa');
7
5
  const cac = require('cac');
8
6
  const index = require('./index.cjs');
9
7
  require('ohmyfetch');
10
8
  require('convert-gitmoji');
9
+ require('node:fs');
11
10
  require('node:module');
12
11
  require('node:url');
13
12
  require('node:assert');
@@ -20,7 +19,7 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
20
19
 
21
20
  const cac__default = /*#__PURE__*/_interopDefaultCompat(cac);
22
21
 
23
- const version = "0.2.0";
22
+ const version = "0.2.2";
24
23
 
25
24
  const cli = cac__default("githublogen");
26
25
  cli.version(version).option("-t, --token <path>", "GitHub Token").option("--from <ref>", "From tag").option("--to <ref>", "To tag").option("--github <path>", "GitHub Repository, e.g. soybeanjs/githublogen").option("--name <name>", "Name of the release").option("--contributors", "Show contributors section").option("--prerelease", "Mark release as prerelease").option("-d, --draft", "Mark release as draft").option("--output <path>", "Output to file instead of sending to GitHub").option("--capitalize", "Should capitalize for each comment message").option("--emoji", "Use emojis in section titles", { default: true }).option("--group", "Nest commit messages under their scopes").option("--dry", "Dry run").help();
@@ -29,7 +28,7 @@ cli.command("").action(async (args) => {
29
28
  console.log();
30
29
  console.log(kolorist.dim(`${kolorist.bold("github")}logen `) + kolorist.dim(`v${version}`));
31
30
  const cwd = process.cwd();
32
- const { config, md, changelog, commits } = await index.generate(cwd, args);
31
+ const { config, md, commits } = await index.generate(cwd, args);
33
32
  const markdown = md.replace(/&nbsp;/g, "");
34
33
  console.log(kolorist.cyan(config.from) + kolorist.dim(" -> ") + kolorist.blue(config.to) + kolorist.dim(` (${commits.length} commits)`));
35
34
  console.log(kolorist.dim("--------------"));
@@ -41,46 +40,6 @@ cli.command("").action(async (args) => {
41
40
  console.log(kolorist.yellow("Dry run. Release skipped."));
42
41
  return;
43
42
  }
44
- if (typeof config.output === "string") {
45
- const pushUrl = index.getGitPushUrl(config.repo, config.tokens.github);
46
- if (!pushUrl) {
47
- return;
48
- }
49
- let changelogMD;
50
- const changelogPrefix = "# Changelog";
51
- if (fs.existsSync(config.output)) {
52
- console.info(`Updating ${config.output}`);
53
- changelogMD = await fs.promises.readFile(config.output, "utf8");
54
- if (!changelogMD.startsWith(changelogPrefix)) {
55
- changelogMD = `${changelogPrefix}
56
-
57
- ${changelogMD}`;
58
- }
59
- } else {
60
- console.info(`Creating ${config.output}`);
61
- changelogMD = `${changelogPrefix}
62
-
63
- `;
64
- }
65
- const lastEntry = changelogMD.match(/^###?\s+.*$/m);
66
- if (lastEntry) {
67
- changelogMD = `${changelogMD.slice(0, lastEntry.index) + changelog}
68
-
69
- ${changelogMD.slice(lastEntry.index)}`;
70
- } else {
71
- changelogMD += `
72
- ${changelog}
73
-
74
- `;
75
- }
76
- await fs.promises.writeFile(config.output, changelogMD);
77
- const { email = "unknow@unknow.com", name = "unknow" } = commits[0]?.author || {};
78
- await execa.execa("git", ["config", "--global", "user.email", `"${email}"`]);
79
- await execa.execa("git", ["config", "--global", "user.name", `"${name}"`]);
80
- await execa.execa("git", ["add", "."]);
81
- await execa.execa("git", ["commit", "-m docs(projects): CHANGELOG.md"], { cwd });
82
- await execa.execa("git", ["push", pushUrl, `HEAD:${config.gitMainBranch}`], { cwd });
83
- }
84
43
  if (!await index.hasTagOnGitHub(config.to, config)) {
85
44
  console.error(kolorist.yellow(`Current ref "${kolorist.bold(config.to)}" is not available as tags on GitHub. Release skipped.`));
86
45
  process.exitCode = 1;
package/dist/cli.mjs CHANGED
@@ -1,11 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, promises } from 'node:fs';
3
2
  import { dim, bold, cyan, blue, yellow, red } from 'kolorist';
4
- import { execa } from 'execa';
5
3
  import cac from 'cac';
6
- import { generate, getGitPushUrl, hasTagOnGitHub, isRepoShallow, sendRelease } from './index.mjs';
4
+ import { generate, hasTagOnGitHub, isRepoShallow, sendRelease } from './index.mjs';
7
5
  import 'ohmyfetch';
8
6
  import 'convert-gitmoji';
7
+ import 'node:fs';
9
8
  import 'node:module';
10
9
  import 'node:url';
11
10
  import 'node:assert';
@@ -14,7 +13,7 @@ import 'node:path';
14
13
  import 'node:v8';
15
14
  import 'node:util';
16
15
 
17
- const version = "0.2.0";
16
+ const version = "0.2.2";
18
17
 
19
18
  const cli = cac("githublogen");
20
19
  cli.version(version).option("-t, --token <path>", "GitHub Token").option("--from <ref>", "From tag").option("--to <ref>", "To tag").option("--github <path>", "GitHub Repository, e.g. soybeanjs/githublogen").option("--name <name>", "Name of the release").option("--contributors", "Show contributors section").option("--prerelease", "Mark release as prerelease").option("-d, --draft", "Mark release as draft").option("--output <path>", "Output to file instead of sending to GitHub").option("--capitalize", "Should capitalize for each comment message").option("--emoji", "Use emojis in section titles", { default: true }).option("--group", "Nest commit messages under their scopes").option("--dry", "Dry run").help();
@@ -23,7 +22,7 @@ cli.command("").action(async (args) => {
23
22
  console.log();
24
23
  console.log(dim(`${bold("github")}logen `) + dim(`v${version}`));
25
24
  const cwd = process.cwd();
26
- const { config, md, changelog, commits } = await generate(cwd, args);
25
+ const { config, md, commits } = await generate(cwd, args);
27
26
  const markdown = md.replace(/&nbsp;/g, "");
28
27
  console.log(cyan(config.from) + dim(" -> ") + blue(config.to) + dim(` (${commits.length} commits)`));
29
28
  console.log(dim("--------------"));
@@ -35,46 +34,6 @@ cli.command("").action(async (args) => {
35
34
  console.log(yellow("Dry run. Release skipped."));
36
35
  return;
37
36
  }
38
- if (typeof config.output === "string") {
39
- const pushUrl = getGitPushUrl(config.repo, config.tokens.github);
40
- if (!pushUrl) {
41
- return;
42
- }
43
- let changelogMD;
44
- const changelogPrefix = "# Changelog";
45
- if (existsSync(config.output)) {
46
- console.info(`Updating ${config.output}`);
47
- changelogMD = await promises.readFile(config.output, "utf8");
48
- if (!changelogMD.startsWith(changelogPrefix)) {
49
- changelogMD = `${changelogPrefix}
50
-
51
- ${changelogMD}`;
52
- }
53
- } else {
54
- console.info(`Creating ${config.output}`);
55
- changelogMD = `${changelogPrefix}
56
-
57
- `;
58
- }
59
- const lastEntry = changelogMD.match(/^###?\s+.*$/m);
60
- if (lastEntry) {
61
- changelogMD = `${changelogMD.slice(0, lastEntry.index) + changelog}
62
-
63
- ${changelogMD.slice(lastEntry.index)}`;
64
- } else {
65
- changelogMD += `
66
- ${changelog}
67
-
68
- `;
69
- }
70
- await promises.writeFile(config.output, changelogMD);
71
- const { email = "unknow@unknow.com", name = "unknow" } = commits[0]?.author || {};
72
- await execa("git", ["config", "--global", "user.email", `"${email}"`]);
73
- await execa("git", ["config", "--global", "user.name", `"${name}"`]);
74
- await execa("git", ["add", "."]);
75
- await execa("git", ["commit", "-m docs(projects): CHANGELOG.md"], { cwd });
76
- await execa("git", ["push", pushUrl, `HEAD:${config.gitMainBranch}`], { cwd });
77
- }
78
37
  if (!await hasTagOnGitHub(config.to, config)) {
79
38
  console.error(yellow(`Current ref "${bold(config.to)}" is not available as tags on GitHub. Release skipped.`));
80
39
  process.exitCode = 1;
package/dist/index.cjs CHANGED
@@ -58,9 +58,6 @@ function join$1(array, glue = ", ", finalGlue = " and ") {
58
58
  return array.join(finalGlue);
59
59
  return `${array.slice(0, -1).join(glue)}${finalGlue}${array.slice(-1)}`;
60
60
  }
61
- function upperFirst(string) {
62
- return !string ? "" : string[0].toUpperCase() + string.slice(1);
63
- }
64
61
 
65
62
  async function sendRelease(options, content) {
66
63
  const headers = getHeaders(options);
@@ -258,6 +255,92 @@ function getGitPushUrl(config, token) {
258
255
  return `https://${token}@${config.domain}/${config.repo}`;
259
256
  }
260
257
 
258
+ const emojisRE = /([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g;
259
+ function formatReferences(references, github, type) {
260
+ const refs = references.filter((i) => {
261
+ if (type === "issues") {
262
+ return i.type === "issue" || i.type === "pull-request";
263
+ }
264
+ return i.type === "hash";
265
+ }).map((ref) => {
266
+ if (!github) {
267
+ return ref.value;
268
+ }
269
+ if (ref.type === "pull-request" || ref.type === "issue") {
270
+ return `https://github.com/${github}/issues/${ref.value.slice(1)}`;
271
+ }
272
+ return `[<samp>(${ref.value.slice(0, 5)})</samp>](https://github.com/${github}/commit/${ref.value})`;
273
+ });
274
+ const referencesString = join$1(refs).trim();
275
+ if (type === "issues") {
276
+ return referencesString && `in ${referencesString}`;
277
+ }
278
+ return referencesString;
279
+ }
280
+ function formatLine(commit, options) {
281
+ const prRefs = formatReferences(commit.references, options.repo.repo || "", "issues");
282
+ const hashRefs = formatReferences(commit.references, options.repo.repo || "", "hash");
283
+ let authors = join$1([
284
+ ...new Set(commit.resolvedAuthors?.map((i) => i.login ? `@${i.login}` : `**${i.name}**`))
285
+ ])?.trim();
286
+ if (authors) {
287
+ authors = `by ${authors}`;
288
+ }
289
+ let refs = [authors, prRefs, hashRefs].filter((i) => i?.trim()).join(" ");
290
+ if (refs) {
291
+ refs = `&nbsp;-&nbsp; ${refs}`;
292
+ }
293
+ const description = options.capitalize ? capitalize(commit.description) : commit.description;
294
+ return [description, refs].filter((i) => i?.trim()).join(" ");
295
+ }
296
+ function formatTitle(name, options) {
297
+ let $name = name.trim();
298
+ if (!options.emoji) {
299
+ $name = name.replace(emojisRE, "").trim();
300
+ }
301
+ return `### &nbsp;&nbsp;&nbsp;${$name}`;
302
+ }
303
+ function formatSection(commits, sectionName, options) {
304
+ if (!commits.length) {
305
+ return [];
306
+ }
307
+ const lines = ["", formatTitle(sectionName, options), ""];
308
+ const scopes = groupBy(commits, "scope");
309
+ let useScopeGroup = options.group;
310
+ if (!Object.entries(scopes).some(([k, v]) => k && v.length > 1)) {
311
+ useScopeGroup = false;
312
+ }
313
+ Object.keys(scopes).sort().forEach((scope) => {
314
+ let padding = "";
315
+ let prefix = "";
316
+ const scopeText = `**${options.scopeMap[scope] || scope}**`;
317
+ if (scope && (useScopeGroup === true || useScopeGroup === "multiple" && scopes[scope].length > 1)) {
318
+ lines.push(`- ${scopeText}:`);
319
+ padding = " ";
320
+ } else if (scope) {
321
+ prefix = `${scopeText}: `;
322
+ }
323
+ lines.push(...scopes[scope].reverse().map((commit) => `${padding}- ${prefix}${formatLine(commit, options)}`));
324
+ });
325
+ return lines;
326
+ }
327
+ function generateMarkdown(commits, options) {
328
+ const lines = [];
329
+ const [breaking, changes] = partition(commits, (c) => c.isBreaking);
330
+ const group = groupBy(changes, "type");
331
+ lines.push(...formatSection(breaking, options.titles.breakingChanges, options));
332
+ for (const type of Object.keys(options.types)) {
333
+ const items = group[type] || [];
334
+ lines.push(...formatSection(items, options.types[type].title, options));
335
+ }
336
+ if (!lines.length) {
337
+ lines.push("*No significant changes*");
338
+ }
339
+ const url = `https://github.com/${options.repo.repo}/compare/${options.from}...${options.to}`;
340
+ lines.push("", `##### &nbsp;&nbsp;&nbsp;&nbsp;[View changes on GitHub](${url})`);
341
+ return convertGitmoji.convert(lines.join("\n").trim(), true);
342
+ }
343
+
261
344
  function normalizeWindowsPath(input = "") {
262
345
  if (!input || !input.includes("\\")) {
263
346
  return input;
@@ -8064,15 +8147,6 @@ async function resolvePackageJSON(id = process.cwd(), options = {}) {
8064
8147
  });
8065
8148
  }
8066
8149
 
8067
- const providerToRefSpec = {
8068
- github: { "pull-request": "pull", hash: "commit", issue: "issues" },
8069
- gitlab: { "pull-request": "merge_requests", hash: "commit", issue: "issues" },
8070
- bitbucket: {
8071
- "pull-request": "pull-requests",
8072
- hash: "commit",
8073
- issue: "issues"
8074
- }
8075
- };
8076
8150
  const providerToDomain = {
8077
8151
  github: "github.com",
8078
8152
  gitlab: "gitlab.com",
@@ -8084,16 +8158,6 @@ const domainToProvider = {
8084
8158
  "bitbucket.org": "bitbucket"
8085
8159
  };
8086
8160
  const providerURLRegex = /^(?:(?<user>\w+)@)?(?:(?<provider>[^/:]+):)?(?<repo>\w+\/\w+)(?:\.git)?$/;
8087
- function baseUrl$1(config) {
8088
- return `https://${config.domain}/${config.repo}`;
8089
- }
8090
- function formatReference(ref, repo) {
8091
- if (!repo?.provider || !(repo.provider in providerToRefSpec)) {
8092
- return ref.value;
8093
- }
8094
- const refSpec = providerToRefSpec[repo.provider];
8095
- return `[${ref.value}](${baseUrl$1(repo)}/${refSpec[ref.type]}/${ref.value.replace(/^#/, "")})`;
8096
- }
8097
8161
  async function resolveRepoConfig(cwd) {
8098
8162
  const pkg = await readPackageJSON(cwd).catch(() => {
8099
8163
  });
@@ -8138,189 +8202,6 @@ function getRepoConfig(repoUrl = "") {
8138
8202
  };
8139
8203
  }
8140
8204
 
8141
- const emojisRE = /([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g;
8142
- function formatReferences(references, github, type) {
8143
- const refs = references.filter((i) => {
8144
- if (type === "issues") {
8145
- return i.type === "issue" || i.type === "pull-request";
8146
- }
8147
- return i.type === "hash";
8148
- }).map((ref) => {
8149
- if (!github) {
8150
- return ref.value;
8151
- }
8152
- if (ref.type === "pull-request" || ref.type === "issue") {
8153
- return `https://github.com/${github}/issues/${ref.value.slice(1)}`;
8154
- }
8155
- return `[<samp>(${ref.value.slice(0, 5)})</samp>](https://github.com/${github}/commit/${ref.value})`;
8156
- });
8157
- const referencesString = join$1(refs).trim();
8158
- if (type === "issues") {
8159
- return referencesString && `in ${referencesString}`;
8160
- }
8161
- return referencesString;
8162
- }
8163
- function formatLine(commit, options) {
8164
- const prRefs = formatReferences(commit.references, options.repo.repo || "", "issues");
8165
- const hashRefs = formatReferences(commit.references, options.repo.repo || "", "hash");
8166
- let authors = join$1([
8167
- ...new Set(commit.resolvedAuthors?.map((i) => i.login ? `@${i.login}` : `**${i.name}**`))
8168
- ])?.trim();
8169
- if (authors) {
8170
- authors = `by ${authors}`;
8171
- }
8172
- let refs = [authors, prRefs, hashRefs].filter((i) => i?.trim()).join(" ");
8173
- if (refs) {
8174
- refs = `&nbsp;-&nbsp; ${refs}`;
8175
- }
8176
- const description = options.capitalize ? capitalize(commit.description) : commit.description;
8177
- return [description, refs].filter((i) => i?.trim()).join(" ");
8178
- }
8179
- function formatTitle(name, options) {
8180
- let $name = name.trim();
8181
- if (!options.emoji) {
8182
- $name = name.replace(emojisRE, "").trim();
8183
- }
8184
- return `### &nbsp;&nbsp;&nbsp;${$name}`;
8185
- }
8186
- function formatSection(commits, sectionName, options) {
8187
- if (!commits.length) {
8188
- return [];
8189
- }
8190
- const lines = ["", formatTitle(sectionName, options), ""];
8191
- const scopes = groupBy(commits, "scope");
8192
- let useScopeGroup = options.group;
8193
- if (!Object.entries(scopes).some(([k, v]) => k && v.length > 1)) {
8194
- useScopeGroup = false;
8195
- }
8196
- Object.keys(scopes).sort().forEach((scope) => {
8197
- let padding = "";
8198
- let prefix = "";
8199
- const scopeText = `**${options.scopeMap[scope] || scope}**`;
8200
- if (scope && (useScopeGroup === true || useScopeGroup === "multiple" && scopes[scope].length > 1)) {
8201
- lines.push(`- ${scopeText}:`);
8202
- padding = " ";
8203
- } else if (scope) {
8204
- prefix = `${scopeText}: `;
8205
- }
8206
- lines.push(...scopes[scope].reverse().map((commit) => `${padding}- ${prefix}${formatLine(commit, options)}`));
8207
- });
8208
- return lines;
8209
- }
8210
- function generateMarkdown(commits, options) {
8211
- const lines = [];
8212
- const [breaking, changes] = partition(commits, (c) => c.isBreaking);
8213
- const group = groupBy(changes, "type");
8214
- lines.push(...formatSection(breaking, options.titles.breakingChanges, options));
8215
- for (const type of Object.keys(options.types)) {
8216
- const items = group[type] || [];
8217
- lines.push(...formatSection(items, options.types[type].title, options));
8218
- }
8219
- if (!lines.length) {
8220
- lines.push("*No significant changes*");
8221
- }
8222
- const url = `https://github.com/${options.repo.repo}/compare/${options.from}...${options.to}`;
8223
- lines.push("", `##### &nbsp;&nbsp;&nbsp;&nbsp;[View changes on GitHub](${url})`);
8224
- return convertGitmoji.convert(lines.join("\n").trim(), true);
8225
- }
8226
- async function generateChangelog(commits, config) {
8227
- const typeGroups = groupBy(commits, "type");
8228
- const markdown = [];
8229
- const breakingChanges = [];
8230
- const v = config.newVersion && `v${config.newVersion}`;
8231
- markdown.push("", `## ${v || `${config.from || ""}...${config.to}`}`, "");
8232
- if (config.repo && config.from) {
8233
- markdown.push(formatCompareChanges(v, config));
8234
- }
8235
- const typeKeys = Object.keys(config.types);
8236
- typeKeys.forEach((typeKey) => {
8237
- const group = typeGroups[typeKey];
8238
- if (group?.length) {
8239
- markdown.push("", `### ${config.types[typeKey].title}`, "");
8240
- for (const commit of group.reverse()) {
8241
- const line = formatCommit(commit, config);
8242
- markdown.push(line);
8243
- if (commit.isBreaking) {
8244
- breakingChanges.push(line);
8245
- }
8246
- }
8247
- }
8248
- });
8249
- if (breakingChanges.length > 0) {
8250
- markdown.push("", "#### \u26A0\uFE0F Breaking Changes", "", ...breakingChanges);
8251
- }
8252
- const authorMap = /* @__PURE__ */ new Map();
8253
- commits.forEach((commit) => {
8254
- const name = formatName(commit.author?.name || "");
8255
- if (name && !name.includes("[bot]")) {
8256
- if (!authorMap.has(name)) {
8257
- authorMap.set(name, { email: /* @__PURE__ */ new Set([commit.author.email]) });
8258
- } else {
8259
- const entry = authorMap.get(name);
8260
- entry?.email?.add(commit.author.email);
8261
- }
8262
- }
8263
- });
8264
- await Promise.all(
8265
- [...authorMap.keys()].map(async (authorName) => {
8266
- const meta = authorMap.get(authorName);
8267
- if (meta) {
8268
- for (const email of meta.email) {
8269
- try {
8270
- const { user } = await ohmyfetch.$fetch(`https://ungh.cc/users/find/${email}`);
8271
- if (user) {
8272
- meta.github = user.username;
8273
- break;
8274
- }
8275
- } catch {
8276
- }
8277
- }
8278
- }
8279
- })
8280
- );
8281
- const authors = [...authorMap.entries()].map((e) => ({ name: e[0], ...e[1] }));
8282
- if (authors.length > 0) {
8283
- markdown.push(
8284
- "",
8285
- "### \u2764\uFE0F Contributors",
8286
- "",
8287
- ...authors.map((i) => {
8288
- const $email = [...i.email].find((e) => !e.includes("noreply.github.com"));
8289
- const email = $email ? `<${$email}>` : "";
8290
- const github = i.github ? `([@${i.github}](http://github.com/${i.github}))` : "";
8291
- return `- ${i.name} ${github || email}`;
8292
- })
8293
- );
8294
- }
8295
- return convertGitmoji.convert(markdown.join("\n").trim(), true);
8296
- }
8297
- function baseUrl(config) {
8298
- return `https://${config.domain}/${config.repo}`;
8299
- }
8300
- function formatCompareChanges(v, config) {
8301
- const part = config.repo.provider === "bitbucket" ? "branches/compare" : "compare";
8302
- return `[compare changes](${baseUrl(config.repo)}/${part}/${config.from}...${v || config.to})`;
8303
- }
8304
- function formatCommit(commit, config) {
8305
- return `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ""}${commit.isBreaking ? "\u26A0\uFE0F " : ""}${upperFirst(
8306
- commit.description
8307
- )}${formatMultiReference(commit.references, config)}`;
8308
- }
8309
- function formatName(name = "") {
8310
- return name.split(" ").map((p) => upperFirst(p.trim())).join(" ");
8311
- }
8312
- function formatMultiReference(references, config) {
8313
- const pr = references.filter((ref) => ref.type === "pull-request");
8314
- const issue = references.filter((ref) => ref.type === "issue");
8315
- if (pr.length > 0 || issue.length > 0) {
8316
- return ` (${[...pr, ...issue].map((ref) => formatReference(ref, config.repo)).join(", ")})`;
8317
- }
8318
- if (references.length > 0) {
8319
- return ` (${formatReference(references[0], config.repo)})`;
8320
- }
8321
- return "";
8322
- }
8323
-
8324
8205
  const defaultConfig = {
8325
8206
  cwd: process.cwd(),
8326
8207
  from: "",
@@ -8427,12 +8308,10 @@ async function generate(cwd, options) {
8427
8308
  await resolveAuthors(commits, resolved);
8428
8309
  }
8429
8310
  const md = generateMarkdown(commits, resolved);
8430
- const changelog = await generateChangelog(commits, resolved);
8431
- return { config: resolved, md, commits, changelog };
8311
+ return { config: resolved, md, commits };
8432
8312
  }
8433
8313
 
8434
8314
  exports.generate = generate;
8435
- exports.generateChangelog = generateChangelog;
8436
8315
  exports.generateMarkdown = generateMarkdown;
8437
8316
  exports.getCurrentGitBranch = getCurrentGitBranch;
8438
8317
  exports.getFirstGitCommit = getFirstGitCommit;
package/dist/index.d.ts CHANGED
@@ -129,13 +129,11 @@ declare function getGitRemoteURL(cwd: string, remote?: string): Promise<string>;
129
129
  declare function getGitPushUrl(config: RepoConfig, token?: string): string | null;
130
130
 
131
131
  declare function generateMarkdown(commits: Commit[], options: ResolvedChangelogOptions): string;
132
- declare function generateChangelog(commits: Commit[], config: ResolvedChangelogOptions): Promise<string>;
133
132
 
134
133
  declare function generate(cwd: string, options: ChangelogOptions): Promise<{
135
134
  config: Required<ChangelogOptions>;
136
135
  md: string;
137
136
  commits: GitCommit[];
138
- changelog: string;
139
137
  }>;
140
138
 
141
139
  declare function resolveConfig(cwd: string, options: ChangelogOptions): Promise<Required<ChangelogOptions>>;
@@ -143,4 +141,4 @@ declare function resolveConfig(cwd: string, options: ChangelogOptions): Promise<
143
141
  declare function parseGitCommit(commit: RawGitCommit, config: ChangelogConfig): GitCommit | null;
144
142
  declare function parseCommits(commits: RawGitCommit[], config: ChangelogConfig): GitCommit[];
145
143
 
146
- export { AuthorInfo, ChangelogConfig, ChangelogOptions, Commit, GitCommit, GitCommitAuthor, GithubOptions, GithubRelease, RawGitCommit, Reference, RepoConfig, RepoProvider, ResolvedChangelogOptions, SemverBumpType, generate, generateChangelog, generateMarkdown, getCurrentGitBranch, getFirstGitCommit, getGitDiff, getGitHubRepo, getGitMainBranchName, getGitPushUrl, getGitRemoteURL, getLastGitTag, hasTagOnGitHub, isPrerelease, isRefGitTag, isRepoShallow, parseCommits, parseGitCommit, resolveAuthorInfo, resolveAuthors, resolveConfig, sendRelease };
144
+ export { AuthorInfo, ChangelogConfig, ChangelogOptions, Commit, GitCommit, GitCommitAuthor, GithubOptions, GithubRelease, RawGitCommit, Reference, RepoConfig, RepoProvider, ResolvedChangelogOptions, SemverBumpType, generate, generateMarkdown, getCurrentGitBranch, getFirstGitCommit, getGitDiff, getGitHubRepo, getGitMainBranchName, getGitPushUrl, getGitRemoteURL, getLastGitTag, hasTagOnGitHub, isPrerelease, isRefGitTag, isRepoShallow, parseCommits, parseGitCommit, resolveAuthorInfo, resolveAuthors, resolveConfig, sendRelease };
package/dist/index.mjs CHANGED
@@ -48,9 +48,6 @@ function join$1(array, glue = ", ", finalGlue = " and ") {
48
48
  return array.join(finalGlue);
49
49
  return `${array.slice(0, -1).join(glue)}${finalGlue}${array.slice(-1)}`;
50
50
  }
51
- function upperFirst(string) {
52
- return !string ? "" : string[0].toUpperCase() + string.slice(1);
53
- }
54
51
 
55
52
  async function sendRelease(options, content) {
56
53
  const headers = getHeaders(options);
@@ -248,6 +245,92 @@ function getGitPushUrl(config, token) {
248
245
  return `https://${token}@${config.domain}/${config.repo}`;
249
246
  }
250
247
 
248
+ const emojisRE = /([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g;
249
+ function formatReferences(references, github, type) {
250
+ const refs = references.filter((i) => {
251
+ if (type === "issues") {
252
+ return i.type === "issue" || i.type === "pull-request";
253
+ }
254
+ return i.type === "hash";
255
+ }).map((ref) => {
256
+ if (!github) {
257
+ return ref.value;
258
+ }
259
+ if (ref.type === "pull-request" || ref.type === "issue") {
260
+ return `https://github.com/${github}/issues/${ref.value.slice(1)}`;
261
+ }
262
+ return `[<samp>(${ref.value.slice(0, 5)})</samp>](https://github.com/${github}/commit/${ref.value})`;
263
+ });
264
+ const referencesString = join$1(refs).trim();
265
+ if (type === "issues") {
266
+ return referencesString && `in ${referencesString}`;
267
+ }
268
+ return referencesString;
269
+ }
270
+ function formatLine(commit, options) {
271
+ const prRefs = formatReferences(commit.references, options.repo.repo || "", "issues");
272
+ const hashRefs = formatReferences(commit.references, options.repo.repo || "", "hash");
273
+ let authors = join$1([
274
+ ...new Set(commit.resolvedAuthors?.map((i) => i.login ? `@${i.login}` : `**${i.name}**`))
275
+ ])?.trim();
276
+ if (authors) {
277
+ authors = `by ${authors}`;
278
+ }
279
+ let refs = [authors, prRefs, hashRefs].filter((i) => i?.trim()).join(" ");
280
+ if (refs) {
281
+ refs = `&nbsp;-&nbsp; ${refs}`;
282
+ }
283
+ const description = options.capitalize ? capitalize(commit.description) : commit.description;
284
+ return [description, refs].filter((i) => i?.trim()).join(" ");
285
+ }
286
+ function formatTitle(name, options) {
287
+ let $name = name.trim();
288
+ if (!options.emoji) {
289
+ $name = name.replace(emojisRE, "").trim();
290
+ }
291
+ return `### &nbsp;&nbsp;&nbsp;${$name}`;
292
+ }
293
+ function formatSection(commits, sectionName, options) {
294
+ if (!commits.length) {
295
+ return [];
296
+ }
297
+ const lines = ["", formatTitle(sectionName, options), ""];
298
+ const scopes = groupBy(commits, "scope");
299
+ let useScopeGroup = options.group;
300
+ if (!Object.entries(scopes).some(([k, v]) => k && v.length > 1)) {
301
+ useScopeGroup = false;
302
+ }
303
+ Object.keys(scopes).sort().forEach((scope) => {
304
+ let padding = "";
305
+ let prefix = "";
306
+ const scopeText = `**${options.scopeMap[scope] || scope}**`;
307
+ if (scope && (useScopeGroup === true || useScopeGroup === "multiple" && scopes[scope].length > 1)) {
308
+ lines.push(`- ${scopeText}:`);
309
+ padding = " ";
310
+ } else if (scope) {
311
+ prefix = `${scopeText}: `;
312
+ }
313
+ lines.push(...scopes[scope].reverse().map((commit) => `${padding}- ${prefix}${formatLine(commit, options)}`));
314
+ });
315
+ return lines;
316
+ }
317
+ function generateMarkdown(commits, options) {
318
+ const lines = [];
319
+ const [breaking, changes] = partition(commits, (c) => c.isBreaking);
320
+ const group = groupBy(changes, "type");
321
+ lines.push(...formatSection(breaking, options.titles.breakingChanges, options));
322
+ for (const type of Object.keys(options.types)) {
323
+ const items = group[type] || [];
324
+ lines.push(...formatSection(items, options.types[type].title, options));
325
+ }
326
+ if (!lines.length) {
327
+ lines.push("*No significant changes*");
328
+ }
329
+ const url = `https://github.com/${options.repo.repo}/compare/${options.from}...${options.to}`;
330
+ lines.push("", `##### &nbsp;&nbsp;&nbsp;&nbsp;[View changes on GitHub](${url})`);
331
+ return convert(lines.join("\n").trim(), true);
332
+ }
333
+
251
334
  function normalizeWindowsPath(input = "") {
252
335
  if (!input || !input.includes("\\")) {
253
336
  return input;
@@ -8054,15 +8137,6 @@ async function resolvePackageJSON(id = process.cwd(), options = {}) {
8054
8137
  });
8055
8138
  }
8056
8139
 
8057
- const providerToRefSpec = {
8058
- github: { "pull-request": "pull", hash: "commit", issue: "issues" },
8059
- gitlab: { "pull-request": "merge_requests", hash: "commit", issue: "issues" },
8060
- bitbucket: {
8061
- "pull-request": "pull-requests",
8062
- hash: "commit",
8063
- issue: "issues"
8064
- }
8065
- };
8066
8140
  const providerToDomain = {
8067
8141
  github: "github.com",
8068
8142
  gitlab: "gitlab.com",
@@ -8074,16 +8148,6 @@ const domainToProvider = {
8074
8148
  "bitbucket.org": "bitbucket"
8075
8149
  };
8076
8150
  const providerURLRegex = /^(?:(?<user>\w+)@)?(?:(?<provider>[^/:]+):)?(?<repo>\w+\/\w+)(?:\.git)?$/;
8077
- function baseUrl$1(config) {
8078
- return `https://${config.domain}/${config.repo}`;
8079
- }
8080
- function formatReference(ref, repo) {
8081
- if (!repo?.provider || !(repo.provider in providerToRefSpec)) {
8082
- return ref.value;
8083
- }
8084
- const refSpec = providerToRefSpec[repo.provider];
8085
- return `[${ref.value}](${baseUrl$1(repo)}/${refSpec[ref.type]}/${ref.value.replace(/^#/, "")})`;
8086
- }
8087
8151
  async function resolveRepoConfig(cwd) {
8088
8152
  const pkg = await readPackageJSON(cwd).catch(() => {
8089
8153
  });
@@ -8128,189 +8192,6 @@ function getRepoConfig(repoUrl = "") {
8128
8192
  };
8129
8193
  }
8130
8194
 
8131
- const emojisRE = /([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g;
8132
- function formatReferences(references, github, type) {
8133
- const refs = references.filter((i) => {
8134
- if (type === "issues") {
8135
- return i.type === "issue" || i.type === "pull-request";
8136
- }
8137
- return i.type === "hash";
8138
- }).map((ref) => {
8139
- if (!github) {
8140
- return ref.value;
8141
- }
8142
- if (ref.type === "pull-request" || ref.type === "issue") {
8143
- return `https://github.com/${github}/issues/${ref.value.slice(1)}`;
8144
- }
8145
- return `[<samp>(${ref.value.slice(0, 5)})</samp>](https://github.com/${github}/commit/${ref.value})`;
8146
- });
8147
- const referencesString = join$1(refs).trim();
8148
- if (type === "issues") {
8149
- return referencesString && `in ${referencesString}`;
8150
- }
8151
- return referencesString;
8152
- }
8153
- function formatLine(commit, options) {
8154
- const prRefs = formatReferences(commit.references, options.repo.repo || "", "issues");
8155
- const hashRefs = formatReferences(commit.references, options.repo.repo || "", "hash");
8156
- let authors = join$1([
8157
- ...new Set(commit.resolvedAuthors?.map((i) => i.login ? `@${i.login}` : `**${i.name}**`))
8158
- ])?.trim();
8159
- if (authors) {
8160
- authors = `by ${authors}`;
8161
- }
8162
- let refs = [authors, prRefs, hashRefs].filter((i) => i?.trim()).join(" ");
8163
- if (refs) {
8164
- refs = `&nbsp;-&nbsp; ${refs}`;
8165
- }
8166
- const description = options.capitalize ? capitalize(commit.description) : commit.description;
8167
- return [description, refs].filter((i) => i?.trim()).join(" ");
8168
- }
8169
- function formatTitle(name, options) {
8170
- let $name = name.trim();
8171
- if (!options.emoji) {
8172
- $name = name.replace(emojisRE, "").trim();
8173
- }
8174
- return `### &nbsp;&nbsp;&nbsp;${$name}`;
8175
- }
8176
- function formatSection(commits, sectionName, options) {
8177
- if (!commits.length) {
8178
- return [];
8179
- }
8180
- const lines = ["", formatTitle(sectionName, options), ""];
8181
- const scopes = groupBy(commits, "scope");
8182
- let useScopeGroup = options.group;
8183
- if (!Object.entries(scopes).some(([k, v]) => k && v.length > 1)) {
8184
- useScopeGroup = false;
8185
- }
8186
- Object.keys(scopes).sort().forEach((scope) => {
8187
- let padding = "";
8188
- let prefix = "";
8189
- const scopeText = `**${options.scopeMap[scope] || scope}**`;
8190
- if (scope && (useScopeGroup === true || useScopeGroup === "multiple" && scopes[scope].length > 1)) {
8191
- lines.push(`- ${scopeText}:`);
8192
- padding = " ";
8193
- } else if (scope) {
8194
- prefix = `${scopeText}: `;
8195
- }
8196
- lines.push(...scopes[scope].reverse().map((commit) => `${padding}- ${prefix}${formatLine(commit, options)}`));
8197
- });
8198
- return lines;
8199
- }
8200
- function generateMarkdown(commits, options) {
8201
- const lines = [];
8202
- const [breaking, changes] = partition(commits, (c) => c.isBreaking);
8203
- const group = groupBy(changes, "type");
8204
- lines.push(...formatSection(breaking, options.titles.breakingChanges, options));
8205
- for (const type of Object.keys(options.types)) {
8206
- const items = group[type] || [];
8207
- lines.push(...formatSection(items, options.types[type].title, options));
8208
- }
8209
- if (!lines.length) {
8210
- lines.push("*No significant changes*");
8211
- }
8212
- const url = `https://github.com/${options.repo.repo}/compare/${options.from}...${options.to}`;
8213
- lines.push("", `##### &nbsp;&nbsp;&nbsp;&nbsp;[View changes on GitHub](${url})`);
8214
- return convert(lines.join("\n").trim(), true);
8215
- }
8216
- async function generateChangelog(commits, config) {
8217
- const typeGroups = groupBy(commits, "type");
8218
- const markdown = [];
8219
- const breakingChanges = [];
8220
- const v = config.newVersion && `v${config.newVersion}`;
8221
- markdown.push("", `## ${v || `${config.from || ""}...${config.to}`}`, "");
8222
- if (config.repo && config.from) {
8223
- markdown.push(formatCompareChanges(v, config));
8224
- }
8225
- const typeKeys = Object.keys(config.types);
8226
- typeKeys.forEach((typeKey) => {
8227
- const group = typeGroups[typeKey];
8228
- if (group?.length) {
8229
- markdown.push("", `### ${config.types[typeKey].title}`, "");
8230
- for (const commit of group.reverse()) {
8231
- const line = formatCommit(commit, config);
8232
- markdown.push(line);
8233
- if (commit.isBreaking) {
8234
- breakingChanges.push(line);
8235
- }
8236
- }
8237
- }
8238
- });
8239
- if (breakingChanges.length > 0) {
8240
- markdown.push("", "#### \u26A0\uFE0F Breaking Changes", "", ...breakingChanges);
8241
- }
8242
- const authorMap = /* @__PURE__ */ new Map();
8243
- commits.forEach((commit) => {
8244
- const name = formatName(commit.author?.name || "");
8245
- if (name && !name.includes("[bot]")) {
8246
- if (!authorMap.has(name)) {
8247
- authorMap.set(name, { email: /* @__PURE__ */ new Set([commit.author.email]) });
8248
- } else {
8249
- const entry = authorMap.get(name);
8250
- entry?.email?.add(commit.author.email);
8251
- }
8252
- }
8253
- });
8254
- await Promise.all(
8255
- [...authorMap.keys()].map(async (authorName) => {
8256
- const meta = authorMap.get(authorName);
8257
- if (meta) {
8258
- for (const email of meta.email) {
8259
- try {
8260
- const { user } = await $fetch(`https://ungh.cc/users/find/${email}`);
8261
- if (user) {
8262
- meta.github = user.username;
8263
- break;
8264
- }
8265
- } catch {
8266
- }
8267
- }
8268
- }
8269
- })
8270
- );
8271
- const authors = [...authorMap.entries()].map((e) => ({ name: e[0], ...e[1] }));
8272
- if (authors.length > 0) {
8273
- markdown.push(
8274
- "",
8275
- "### \u2764\uFE0F Contributors",
8276
- "",
8277
- ...authors.map((i) => {
8278
- const $email = [...i.email].find((e) => !e.includes("noreply.github.com"));
8279
- const email = $email ? `<${$email}>` : "";
8280
- const github = i.github ? `([@${i.github}](http://github.com/${i.github}))` : "";
8281
- return `- ${i.name} ${github || email}`;
8282
- })
8283
- );
8284
- }
8285
- return convert(markdown.join("\n").trim(), true);
8286
- }
8287
- function baseUrl(config) {
8288
- return `https://${config.domain}/${config.repo}`;
8289
- }
8290
- function formatCompareChanges(v, config) {
8291
- const part = config.repo.provider === "bitbucket" ? "branches/compare" : "compare";
8292
- return `[compare changes](${baseUrl(config.repo)}/${part}/${config.from}...${v || config.to})`;
8293
- }
8294
- function formatCommit(commit, config) {
8295
- return `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ""}${commit.isBreaking ? "\u26A0\uFE0F " : ""}${upperFirst(
8296
- commit.description
8297
- )}${formatMultiReference(commit.references, config)}`;
8298
- }
8299
- function formatName(name = "") {
8300
- return name.split(" ").map((p) => upperFirst(p.trim())).join(" ");
8301
- }
8302
- function formatMultiReference(references, config) {
8303
- const pr = references.filter((ref) => ref.type === "pull-request");
8304
- const issue = references.filter((ref) => ref.type === "issue");
8305
- if (pr.length > 0 || issue.length > 0) {
8306
- return ` (${[...pr, ...issue].map((ref) => formatReference(ref, config.repo)).join(", ")})`;
8307
- }
8308
- if (references.length > 0) {
8309
- return ` (${formatReference(references[0], config.repo)})`;
8310
- }
8311
- return "";
8312
- }
8313
-
8314
8195
  const defaultConfig = {
8315
8196
  cwd: process.cwd(),
8316
8197
  from: "",
@@ -8417,8 +8298,7 @@ async function generate(cwd, options) {
8417
8298
  await resolveAuthors(commits, resolved);
8418
8299
  }
8419
8300
  const md = generateMarkdown(commits, resolved);
8420
- const changelog = await generateChangelog(commits, resolved);
8421
- return { config: resolved, md, commits, changelog };
8301
+ return { config: resolved, md, commits };
8422
8302
  }
8423
8303
 
8424
- export { generate, generateChangelog, generateMarkdown, getCurrentGitBranch, getFirstGitCommit, getGitDiff, getGitHubRepo, getGitMainBranchName, getGitPushUrl, getGitRemoteURL, getLastGitTag, hasTagOnGitHub, isPrerelease, isRefGitTag, isRepoShallow, parseCommits, parseGitCommit, resolveAuthorInfo, resolveAuthors, resolveConfig, sendRelease };
8304
+ export { generate, generateMarkdown, getCurrentGitBranch, getFirstGitCommit, getGitDiff, getGitHubRepo, getGitMainBranchName, getGitPushUrl, getGitRemoteURL, getLastGitTag, hasTagOnGitHub, isPrerelease, isRefGitTag, isRepoShallow, parseCommits, parseGitCommit, resolveAuthorInfo, resolveAuthors, resolveConfig, sendRelease };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "githublogen",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "sideEffects": false,
6
6
  "author": {
7
7
  "name": "SoybeanJS",
@@ -54,36 +54,29 @@
54
54
  "ohmyfetch": "0.4.21"
55
55
  },
56
56
  "devDependencies": {
57
- "@soybeanjs/cli": "0.2.11",
57
+ "@soybeanjs/cli": "0.5.0",
58
58
  "@types/node": "20.2.5",
59
- "bumpp": "9.1.0",
60
- "changelogen": "^0.5.3",
61
- "eslint": "8.41.0",
62
- "eslint-config-soybeanjs": "0.4.6",
63
- "lint-staged": "13.2.2",
59
+ "eslint": "8.42.0",
60
+ "eslint-config-soybeanjs": "0.4.8",
64
61
  "simple-git-hooks": "2.8.1",
65
- "tsx": "^3.12.7",
66
- "typescript": "5.0.4",
62
+ "tsx": "3.12.7",
63
+ "typescript": "5.1.3",
67
64
  "unbuild": "1.2.1"
68
65
  },
69
66
  "simple-git-hooks": {
70
67
  "commit-msg": "pnpm soy git-commit-verify",
71
- "pre-commit": "pnpm lint-staged"
72
- },
73
- "lint-staged": {
74
- "*.{js,mjs,jsx,ts,mts,tsx,json,vue,svelte}": "eslint . --fix",
75
- "*.!{js,mjs,jsx,ts,mts,tsx,json,vue,svelte}": "format"
68
+ "pre-commit": "pnpm soy lint-staged"
76
69
  },
70
+ "github-token": "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",
77
71
  "scripts": {
78
- "build": "unbuild",
72
+ "build": "pnpm typecheck && unbuild",
79
73
  "lint": "eslint . --fix",
80
74
  "format": "soy prettier-format",
81
75
  "commit": "soy git-commit",
82
76
  "cleanup": "soy cleanup",
83
77
  "update-pkg": "soy update-pkg",
84
- "update-version": "bumpp --commit --push --tag",
85
78
  "publish-pkg": "pnpm -r publish --access public",
86
- "release": "pnpm update-version && pnpm build && pnpm publish-pkg",
79
+ "release": "pnpm soy release && pnpm build && pnpm publish-pkg",
87
80
  "typecheck": "tsc --noEmit"
88
81
  }
89
82
  }