Skip to content

Commit

Permalink
✨ feat(changelog): add changelog style configs (#669)
Browse files Browse the repository at this point in the history
* ✨ feat(changelog): add configs `showAuthorAvatar` `showSummary` `reduceHeadingLevel` `newlineTimestamp` `addBackToTop`

* ✅ test(changelog): fix changelog test

* 🛠️ chore(changelog): clean

* ✨ feat(changelog): better summary style

* ✨ feat(changelog): add summary avatar list

* 💄 style(changelog): better summary layout

* 🐛 fix(changelog): footer position

* 🐛 fix(changelog): fix summary spacing

* 💄 style(changelog): fix typo

* 📝 docs(changlog): update README.md

* ✅ test(changelog): add `finalizeContext` and `transformer` tests
  • Loading branch information
canisminor1990 committed Jun 10, 2023
1 parent 678d734 commit f9e6585
Show file tree
Hide file tree
Showing 17 changed files with 446 additions and 51 deletions.
3 changes: 3 additions & 0 deletions packages/changelog/.changelogrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ const base = require('../../.changelogrc');

module.exports = {
...base,
scopeDisplayName: {
'*': 'misc',
},
};
35 changes: 34 additions & 1 deletion packages/changelog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,48 @@ interface ChangelogConfig {
* title language
* @default en-US
*/
titleLanguage?: 'en-US' | 'zh-CN';
titleLanguage?: 'en-US' | 'zh-CN' | 'mix';
/**
* whether to show author
* @default false
*/
showAuthor?: boolean;
/**
* whether to show author avatar
* @default false
*/
showAuthorAvatar?: boolean;
/**
* whether to show summary
* @default false
*/
showSummary?: boolean;
/**
* Reduce heading level from # to ##
* @default false
*/
reduceHeadingLevel?: boolean;
/**
* put timestamp to second line
* @default false
*/
newlineTimestamp?: boolean;
/**
* add back to top button
* @default false
*/
addBackToTop?: boolean;
}
```

> 👉 Tip: If turn on `back to top` button, should edit `CHANGELOG.md` first like below:
```markdown
<a name="readme-top"></a>

# Changelog
```

## License

[MIT](../../LICENSE) ® Arvin Xu
Expand Down
15 changes: 8 additions & 7 deletions packages/changelog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,18 @@
"dependencies": {
"@gitmoji/commit-types": "1.1.5",
"@gitmoji/parser-opts": "1.4.0",
"cosmiconfig": "^7.0.0"
"cosmiconfig": "^7"
},
"devDependencies": {
"@types/conventional-changelog-core": "^4.1.1",
"better-than-before": "^1.0.0",
"conventional-changelog-core": "^4.2.2",
"@types/conventional-changelog-core": "^4",
"better-than-before": "^1",
"conventional-changelog-core": "^4",
"conventional-changelog-writer": "^5",
"conventional-commits-parser": "^3",
"git-dummy-commit": "^1.3.0",
"shelljs": "^0.8.4",
"through2": "^4.0.2"
"git-dummy-commit": "^1",
"pangu": "^4",
"shelljs": "^0",
"through2": "^4"
},
"publishConfig": {
"access": "public",
Expand Down
20 changes: 20 additions & 0 deletions packages/changelog/src/customConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ export interface CustomConfig {
* 是否显示作者
*/
showAuthor?: boolean;
/**
* 是否显示作者头像
*/
showAuthorAvatar?: boolean;
/**
* 是否显示摘要
*/
showSummary?: boolean;
/**
* whether to include emoji in title
*/
Expand All @@ -26,6 +34,18 @@ export interface CustomConfig {
* title language
*/
titleLanguage?: 'en-US' | 'zh-CN';
/**
* Reduce heading level from # to ##
*/
reduceHeadingLevel?: boolean;
/**
* put timestamp to second line
*/
newlineTimestamp?: boolean;
/**
* add back to top button
*/
addBackToTop?: boolean;
}

const explorer = cosmiconfigSync('changelog');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`test transformCommitGroups should transform commitGroups correctly 1`] = `
{
"authors": [
{
"authorEmail": "test@test.com",
"authorName": "test",
"authorNameEncode": "test",
},
],
"commitGroups": [
{
"commits": [
{
"authorEmail": "test@test.com",
"authorName": "test",
"authorNameEncode": "test",
"first": true,
"hash": "1234",
"last": true,
"scope": "test",
"subject": "test commit",
"title": "✨ Features",
},
],
"subtitle": "What's improved",
"title": "✨ Features",
},
],
}
`;
83 changes: 83 additions & 0 deletions packages/changelog/src/finalizeContext/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { Context } from 'conventional-changelog-writer';
import { CustomConfig } from '../customConfig';
import { typeMap } from '../transformer/typeDisplayName';

describe('test transformCommitGroups', () => {
const customConfig: CustomConfig = {
scopeDisplayName: {
'*': 'all',
},
};
const context: Context = {
commitGroups: [
{
title: '✨ Features',
commits: [
{
hash: '1234',
subject: 'test commit',
scope: 'test',
title: '✨ Features',
authorName: 'test',
authorEmail: 'test@test.com',
authorNameEncode: 'test',
},
],
},
],
authors: [],
};

it('should transform commitGroups correctly', () => {
const subCommitScope = customConfig?.scopeDisplayName?.['*'] || '';
const authors = {};
context.commitGroups = context.commitGroups.map((item) => {
const subtitle = Object.values(typeMap).find(
(i) =>
item.title.includes(i['emoji']) ||
item.title.includes(i['en-US']) ||
item.title.includes(i['zh-CN']),
).subtitle;
let group;
let commits = item.commits.sort((a, b) => {
if (a.scope === subCommitScope && b.scope === subCommitScope) {
return 0;
} else if (a.scope === subCommitScope) {
return 1;
} else if (b.scope === subCommitScope) {
return -1;
} else {
return 0;
}
});
commits = commits.map((c, index) => {
if (group !== c.scope) {
group = c.scope;
c.first = true;
} else {
c.first = false;
}
if (!commits[index + 1] || group !== commits[index + 1].scope) {
c.last = true;
} else {
c.last = false;
}
if (c.authorNameEncode && !authors[c.authorNameEncode]) {
authors[c.authorNameEncode] = {
authorName: c.authorName,
authorEmail: c.authorEmail,
authorNameEncode: c.authorNameEncode,
};
}
return c;
});
return {
title: item.title,
subtitle,
commits,
};
});
context.authors = Object.values(authors);
expect(context).toMatchSnapshot();
});
});
55 changes: 55 additions & 0 deletions packages/changelog/src/finalizeContext/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { Context } from 'conventional-changelog-writer';
import { CustomConfig } from '../customConfig';
import { typeMap } from '../transformer/typeDisplayName';
export default (customConfig: CustomConfig) => (context: Context): Context => {
const subCommitScope = customConfig?.scopeDisplayName?.['*'] || '';
const authors = {};
context.commitGroups = context.commitGroups.map((item) => {
const subtitle = Object.values(typeMap).find(
(i) =>
item.title.includes(i['emoji']) ||
item.title.includes(i['en-US']) ||
item.title.includes(i['zh-CN']),
).subtitle;
let group;
let commits = item.commits.sort((a, b) => {
if (a.scope === subCommitScope && b.scope === subCommitScope) {
return 0;
} else if (a.scope === subCommitScope) {
return 1;
} else if (b.scope === subCommitScope) {
return -1;
} else {
return 0;
}
});
commits = commits.map((c, index) => {
if (group !== c.scope) {
group = c.scope;
c.first = true;
} else {
c.first = false;
}
if (!commits[index + 1] || group !== commits[index + 1].scope) {
c.last = true;
} else {
c.last = false;
}
if (c.authorNameEncode && !authors[c.authorNameEncode]) {
authors[c.authorNameEncode] = {
authorName: c.authorName,
authorEmail: c.authorEmail,
authorNameEncode: c.authorNameEncode,
};
}
return c;
});
return {
title: item.title,
subtitle,
commits,
};
});
context.authors = Object.values(authors);
return context;
};
49 changes: 34 additions & 15 deletions packages/changelog/src/handleWriterOpts.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,47 @@
import type { Options } from 'conventional-changelog-writer';
import { readFileSync } from 'fs';
import { resolve } from 'path';

import type { CustomConfig } from './customConfig';

import finalizeContext from './finalizeContext';
import transformer from './transformer';

const basePath = resolve(__dirname, './templates');

const template = readFileSync(`${basePath}/template.hbs`, 'utf-8');
const summaryTemplate = readFileSync(`${basePath}/summary-template.hbs`, 'utf-8');
const header = readFileSync(`${basePath}/header.hbs`, 'utf-8');
const headerNewlineTimestamp = readFileSync(`${basePath}/header-newline-timestamp.hbs`, 'utf-8');
const commit = readFileSync(`${basePath}/commit.hbs`, 'utf-8');
const footer = readFileSync(`${basePath}/footer.hbs`, 'utf-8');
const author = readFileSync(`${basePath}/author.hbs`, 'utf-8');

export default (customConfig: CustomConfig): Options => ({
transform: transformer(customConfig),
groupBy: 'type',
commitGroupsSort: 'title',
commitsSort: ['scope', 'subject'],
noteGroupsSort: 'title',
mainTemplate: template,
headerPartial: header,
// 替换 commit.hbs 模板中的 gitUserInfo
commitPartial: commit.replace(/{{gitUserInfo}}/g, customConfig.showAuthor ? author : ''),
footerPartial: footer,
});
const authorAvatar = readFileSync(`${basePath}/author-avatar.hbs`, 'utf-8');
const backToTop = readFileSync(`${basePath}/back-to-top.hbs`, 'utf-8');
const reduceHeadingLevel = (skip: boolean, template: string): string => {
if (skip) return template;
return template.replace(/(^|\n)(#+)/g, (match, p1, p2) => p1 + '#' + p2);
};
export default (customConfig: CustomConfig): Options => {
const mainTemplate = customConfig.showSummary ? summaryTemplate : template;
const commitPartial = commit.replace(
/{{gitUserInfo}}/g,
customConfig.showAuthor ? (customConfig.showAuthorAvatar ? authorAvatar : author) : '',
);
const headerPartial = customConfig.newlineTimestamp ? headerNewlineTimestamp : header;
const footerPartial = footer.replace(
/{{backToTop}}/g,
customConfig.addBackToTop ? backToTop : '',
);
return {
transform: transformer(customConfig),
groupBy: 'type',
commitGroupsSort: 'title',
commitsSort: ['scope', 'subject'],
noteGroupsSort: 'title',
mainTemplate: reduceHeadingLevel(!customConfig.reduceHeadingLevel, mainTemplate),
headerPartial: reduceHeadingLevel(!customConfig.reduceHeadingLevel, headerPartial),
// 替换 commit.hbs 模板中的 gitUserInfo
commitPartial: reduceHeadingLevel(!customConfig.reduceHeadingLevel, commitPartial),
footerPartial: reduceHeadingLevel(!customConfig.reduceHeadingLevel, footerPartial),
finalizeContext: finalizeContext(customConfig),
};
};
1 change: 1 addition & 0 deletions packages/changelog/src/templates/author-avatar.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#if authorName}} - by [<img width="16" height="16" src="https://avatars.githubusercontent.com/{{authorNameEncode}}" /> **{{authorName}}**](https://github.com/{{authorNameEncode}}){{/if}}
5 changes: 5 additions & 0 deletions packages/changelog/src/templates/back-to-top.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>

0 comments on commit f9e6585

Please sign in to comment.