Skip to content

Commit 8945cf9

Browse files
committed
feat(changelog): add support for breaking changes in changelog generation
1 parent 16c2e39 commit 8945cf9

2 files changed

Lines changed: 99 additions & 0 deletions

File tree

src/changelog.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ export function changelog({
3535
discard = false
3636
})
3737

38+
const hasBreakingNotes = commit.notes.length > 0
39+
if (typeof commit.type === 'string' && commit.type.endsWith('!')) {
40+
commit.type = commit.type.slice(0, -1)
41+
if (!hasBreakingNotes) {
42+
commit.notes.push({ title: 'BREAKING CHANGES', text: '' })
43+
}
44+
discard = false
45+
}
46+
3847
if (commit.type === 'feat') {
3948
commit.type = 'Features'
4049
} else if (commit.type === 'fix') {

test/changelog.test.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest'
2+
import { changelog } from '../src/changelog'
3+
4+
const fsMock = vi.hoisted(() => {
5+
const createWriteStream = vi.fn(() => ({
6+
on: vi.fn((event: string, callback: () => void) => {
7+
if (event === 'close') {
8+
callback()
9+
}
10+
return undefined
11+
}),
12+
}))
13+
14+
return { createWriteStream }
15+
})
16+
17+
const promptsMock = vi.hoisted(() => {
18+
const spinner = vi.fn(() => ({
19+
start: vi.fn(),
20+
stop: vi.fn(),
21+
cancel: vi.fn(),
22+
}))
23+
24+
return { spinner }
25+
})
26+
27+
const conventionalMock = vi.hoisted(() => {
28+
const state = {
29+
writerOpts: undefined as any,
30+
}
31+
32+
const fn = vi.fn((...args: any[]) => {
33+
state.writerOpts = args[4]
34+
return {
35+
pipe: (dest: any) => dest,
36+
}
37+
})
38+
39+
return { state, fn }
40+
})
41+
42+
vi.mock('fs-extra', () => ({
43+
default: {
44+
createWriteStream: fsMock.createWriteStream,
45+
},
46+
}))
47+
48+
vi.mock('@clack/prompts', () => promptsMock)
49+
50+
vi.mock('conventional-changelog', () => ({
51+
default: conventionalMock.fn,
52+
}))
53+
54+
describe('changelog', () => {
55+
beforeEach(() => {
56+
fsMock.createWriteStream.mockClear()
57+
promptsMock.spinner.mockClear()
58+
conventionalMock.fn.mockClear()
59+
conventionalMock.state.writerOpts = undefined
60+
})
61+
62+
it('keeps feat! commits and adds breaking notes', async () => {
63+
await changelog({ file: 'CHANGELOG.md' })
64+
65+
const transform = conventionalMock.state.writerOpts?.transform
66+
expect(typeof transform).toBe('function')
67+
68+
const commit = {
69+
type: 'feat!',
70+
scope: 'core',
71+
subject: 'add feature',
72+
hash: 'abcdef012345',
73+
notes: [] as Array<{ title: string; text?: string }>,
74+
references: [] as Array<{ issue: string }>,
75+
}
76+
77+
const context = {
78+
host: 'https://github.com',
79+
owner: 'varletjs',
80+
repository: 'release',
81+
}
82+
83+
const result = transform(commit, context)
84+
85+
expect(result).not.toBe(false)
86+
expect(commit.type).toBe('Features')
87+
expect(commit.notes).toHaveLength(1)
88+
expect(commit.notes[0].title).toBe('BREAKING CHANGES')
89+
})
90+
})

0 commit comments

Comments
 (0)