Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 45 additions & 6 deletions src/changelog-notes/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ interface Note {
text: string;
}

const INLINE_CODE_PATTERN = /``[^`].*[^`]``|`[^`]*`/g;

export class DefaultChangelogNotes implements ChangelogNotes {
// allow for customized commit template.
private commitPartial?: string;
Expand Down Expand Up @@ -73,6 +75,8 @@ export class DefaultChangelogNotes implements ChangelogNotes {
this.headerPartial || preset.writerOpts.headerPartial;
preset.writerOpts.mainTemplate =
this.mainTemplate || preset.writerOpts.mainTemplate;
const protectedInlineCode = new Map<string, string>();
let inlineCodeTokenIndex = 0;
const changelogCommits = commits.map(commit => {
const notes = commit.notes
.filter(note => note.title === 'BREAKING CHANGE')
Expand All @@ -84,9 +88,14 @@ export class DefaultChangelogNotes implements ChangelogNotes {
context.repository
)
);
const subject = protectInlineCode(
htmlEscape(commit.bareMessage),
protectedInlineCode,
() => buildInlineCodeToken(inlineCodeTokenIndex++)
);
return {
body: '', // commit.body,
subject: htmlEscape(commit.bareMessage),
subject,
type: commit.type,
scope: commit.scope,
notes,
Expand All @@ -103,9 +112,12 @@ export class DefaultChangelogNotes implements ChangelogNotes {
};
});

return conventionalChangelogWriter
.parseArray(changelogCommits, context, preset.writerOpts)
.trim();
return restoreInlineCode(
conventionalChangelogWriter
.parseArray(changelogCommits, context, preset.writerOpts)
.trim(),
protectedInlineCode
);
}
}

Expand All @@ -123,7 +135,34 @@ function replaceIssueLink(
}

function htmlEscape(message: string): string {
return message.replace(/``[^`].*[^`]``|`[^`]*`|<|>/g, match =>
match.length > 1 ? match : match === '<' ? '&lt;' : '&gt;'
return message.replace(
new RegExp(`${INLINE_CODE_PATTERN.source}|<|>`, 'g'),
match => (match.length > 1 ? match : match === '<' ? '&lt;' : '&gt;')
);
}

function buildInlineCodeToken(index: number): string {
return `INLINE_CODE_TOKEN_${index}`;
}

function protectInlineCode(
message: string,
protectedInlineCode: Map<string, string>,
nextToken: () => string
): string {
return message.replace(INLINE_CODE_PATTERN, match => {
const token = nextToken();
protectedInlineCode.set(token, match);
return token;
});
}

function restoreInlineCode(
message: string,
protectedInlineCode: Map<string, string>
): string {
for (const [token, inlineCode] of protectedInlineCode.entries()) {
message = message.replace(new RegExp(token, 'g'), inlineCode);
}
return message;
}
17 changes: 17 additions & 0 deletions test/changelog-notes/default-changelog-notes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,23 @@ describe('DefaultChangelogNotes', () => {
expect(notes).to.is.string;
safeSnapshot(notes);
});
it('should preserve mention-like tokens in inline code', async () => {
const commits = [
buildMockCommit(
'feat: keep `@spawn` and `@unsafe` literal for @bcoe'
),
];
const changelogNotes = new DefaultChangelogNotes();
const notes = await changelogNotes.buildNotes(
parseConventionalCommits(commits),
notesOptions
);
expect(notes).to.include('`@spawn`');
expect(notes).to.include('`@unsafe`');
expect(notes).to.include('[@bcoe](https://github.com/bcoe)');
expect(notes).to.not.include('[`@spawn`](https://github.com/spawn)');
expect(notes).to.not.include('[`@unsafe`](https://github.com/unsafe)');
});
// it('ignores reverted commits', async () => {
// const commits = [buildCommitFromFixture('multiple-messages')];
// const changelogNotes = new DefaultChangelogNotes();
Expand Down
Loading