Skip to content

Commit

Permalink
feat(conventional-changelog-writer)!: formatDate option (#1189)
Browse files Browse the repository at this point in the history
BREAKING CHANGES: `timeZone` option was removed, please use `formatDate` option to change timezone of a date.

closes #1186
  • Loading branch information
dangreen committed Dec 22, 2023
1 parent 4d15052 commit 8c4bbbe
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 65 deletions.
10 changes: 3 additions & 7 deletions packages/conventional-changelog-writer/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ import type {
CommitGroup,
CommitNote,
NoteGroup,
Options,
FinalOptions,
Context,
FinalContext
} from './types/index.js'
import {
formatDate,
stringify
} from './utils.js'
import { stringify } from './utils.js'

export function getCommitGroups<Commit extends CommitKnownProps = CommitKnownProps>(
commits: Commit[],
Expand Down Expand Up @@ -117,12 +113,12 @@ export function getExtraContext<Commit extends CommitKnownProps = CommitKnownPro
*/
export function getFinalContext<Commit extends CommitKnownProps = CommitKnownProps>(
context: Context<Commit>,
options: Options<Commit>
options: Pick<FinalOptions<Commit>, 'formatDate'>
) {
const finalContext: FinalContext<Commit> = {
commit: 'commits',
issue: 'issues',
date: formatDate(new Date(), options.timeZone),
date: options.formatDate(new Date()),
...context
}

Expand Down
9 changes: 5 additions & 4 deletions packages/conventional-changelog-writer/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ const HEADER_MAX_LENGTH = 100
* @param commit
* @param _context
* @param options
* @param options.timeZone - Time zone for date formatting.
* @param options.formatDate - Date formatter function.
* @returns Patch object for commit.
*/
export function defaultCommitTransform<Commit extends CommitKnownProps = CommitKnownProps>(
commit: Commit,
_context?: unknown,
options?: { timeZone?: string }
_context: unknown,
options: Pick<FinalOptions<Commit>, 'formatDate'>
) {
const {
hash,
Expand All @@ -43,7 +43,7 @@ export function defaultCommitTransform<Commit extends CommitKnownProps = CommitK
? header.substring(0, HEADER_MAX_LENGTH)
: header,
committerDate: committerDate
? formatDate(committerDate, options?.timeZone)
? options.formatDate(committerDate)
: committerDate
} as Partial<Commit>
}
Expand All @@ -67,6 +67,7 @@ export function getFinalOptions<Commit extends CommitKnownProps = CommitKnownPro
generateOn: (commit: Commit) => Boolean(semverValid(commit.version)),
finalizeContext: (context: FinalContext<Commit>) => context,
debug: () => { /* noop */ },
formatDate,
reverse: false,
ignoreReverted: true,
doFlush: true,
Expand Down
53 changes: 35 additions & 18 deletions packages/conventional-changelog-writer/src/template.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ describe('conventional-changelog-writer', () => {

describe('createTemplateRenderer', () => {
it('should merge with the key commit', async () => {
const log = await createTemplateRenderer(getFinalContext({
version: 'a'
}, {}), getFinalOptions({}, {
const finalOptions = getFinalOptions({}, {
...emptyTemplates,
mainTemplate: '{{version}}'
}))([], {
})
const finalContext = getFinalContext({
version: 'a'
}, finalOptions)
const log = await createTemplateRenderer(finalContext, finalOptions)([], {
version: 'b',
notes: []
})
Expand All @@ -86,12 +88,14 @@ describe('conventional-changelog-writer', () => {
})

it('should attach a copy of the commit to note', async () => {
const log = await createTemplateRenderer<Commit>(getFinalContext({}, {}), getFinalOptions({
const finalOptions = getFinalOptions({
ignoreReverted: true
}, {
...emptyTemplates,
mainTemplate: '{{#each noteGroups}}{{#each notes}}{{commit.header}}{{/each}}{{/each}}'
}))([
})
const finalContext = getFinalContext({}, finalOptions)
const log = await createTemplateRenderer<Commit>(finalContext, finalOptions)([
{
header: 'feat(): new feature',
body: null,
Expand Down Expand Up @@ -137,10 +141,12 @@ describe('conventional-changelog-writer', () => {
})

it('should not html escape any content', async () => {
const log = await createTemplateRenderer(getFinalContext({}, {}), getFinalOptions({}, {
const finalOptions = getFinalOptions({}, {
...emptyTemplates,
mainTemplate: '{{version}}'
}))([], {
})
const finalContext = getFinalContext({}, finalOptions)
const log = await createTemplateRenderer(finalContext, finalOptions)([], {
version: '`a`',
notes: []
})
Expand All @@ -149,12 +155,14 @@ describe('conventional-changelog-writer', () => {
})

it('should ignore a reverted commit', async () => {
const log = await createTemplateRenderer<Commit>(getFinalContext({}, {}), getFinalOptions({
const finalOptions = getFinalOptions({
ignoreReverted: true
}, {
...emptyTemplates,
mainTemplate: '{{#each commitGroups}}{{commits.length}}{{#each commits}}{{header}}{{/each}}{{/each}}{{#each noteGroups}}{{title}}{{#each notes}}{{text}}{{/each}}{{/each}}'
}))([
})
const finalContext = getFinalContext({}, finalOptions)
const log = await createTemplateRenderer<Commit>(finalContext, finalOptions)([
{
header: 'revert: feat(): amazing new module\n',
body: 'This reverts commit 56185b7356766d2b30cfa2406b257080272e0b7a.\n',
Expand Down Expand Up @@ -235,15 +243,17 @@ describe('conventional-changelog-writer', () => {
})

it('should finalize context', async () => {
const log = await createTemplateRenderer(getFinalContext({}, {}), getFinalOptions({
const finalOptions = getFinalOptions({
finalizeContext: (context) => {
context.title = 'oh'
return context
}
}, {
...emptyTemplates,
mainTemplate: '{{version}} {{title}}'
}))([], {
})
const finalContext = getFinalContext({}, finalOptions)
const log = await createTemplateRenderer(finalContext, finalOptions)([], {
version: '`a`',
notes: []
})
Expand All @@ -252,7 +262,7 @@ describe('conventional-changelog-writer', () => {
})

it('should support finalize the context async', async () => {
const log = await createTemplateRenderer(getFinalContext({}, {}), getFinalOptions({
const finalOptions = getFinalOptions({
finalizeContext: async (context) => {
await delay(100)
context.title = 'oh'
Expand All @@ -261,7 +271,9 @@ describe('conventional-changelog-writer', () => {
}, {
...emptyTemplates,
mainTemplate: '{{version}} {{title}}'
}))([], {
})
const finalContext = getFinalContext({}, finalOptions)
const log = await createTemplateRenderer(finalContext, finalOptions)([], {
version: '`a`',
notes: []
})
Expand All @@ -270,7 +282,7 @@ describe('conventional-changelog-writer', () => {
})

it('should finalize context', async () => {
const log = await createTemplateRenderer(getFinalContext({}, {}), getFinalOptions({
const finalOptions = getFinalOptions({
finalizeContext: (context, options, commits, keyCommit) => {
context.title = 'oh'
context.date = String(commits.length)
Expand All @@ -281,7 +293,9 @@ describe('conventional-changelog-writer', () => {
}, {
...emptyTemplates,
mainTemplate: '{{version}} {{title}} {{date}} {{version}}'
}))([], {
})
const finalContext = getFinalContext({}, finalOptions)
const log = await createTemplateRenderer(finalContext, finalOptions)([], {
version: '`a`',
notes: []
})
Expand All @@ -290,7 +304,7 @@ describe('conventional-changelog-writer', () => {
})

it('should pass the correct arguments', async () => {
await createTemplateRenderer<Commit>(getFinalContext({}, {}), getFinalOptions({
const finalOptions = getFinalOptions({
ignoreReverted: true,
finalizeContext: (context, options, filteredCommits, keyCommit, originalCommits) => {
expect(filteredCommits.length).toBe(2)
Expand All @@ -301,7 +315,10 @@ describe('conventional-changelog-writer', () => {
}, {
...emptyTemplates,
mainTemplate: '{{#each noteGroups}}{{#each notes}}{{commit.header}}{{/each}}{{/each}}'
}))([
})
const finalContext = getFinalContext({}, finalOptions)

await createTemplateRenderer<Commit>(finalContext, finalOptions)([
{
header: 'revert: feat(): amazing new module\n',
body: 'This reverts commit 56185b7356766d2b30cfa2406b257080272e0b7a.\n',
Expand Down
7 changes: 5 additions & 2 deletions packages/conventional-changelog-writer/src/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,11 @@ export interface Options<Commit extends CommitKnownProps = CommitKnownProps> ext
*/
debug?(message: string): void
/**
* The timezone to use. The date in the changelog is generated based on timezone.
* A function to format date.
* @param date - Date string or Date object.
* @returns Final date string.
*/
timeZone?: string
formatDate?(date: string | Date): string
}

type RequiredOptions<Commit extends CommitKnownProps = CommitKnownProps> = Required<Options<Commit>>
Expand All @@ -121,6 +123,7 @@ export interface FinalOptions<Commit extends CommitKnownProps = CommitKnownProps
generateOn: RequiredOptions<Commit>['generateOn']
finalizeContext: RequiredOptions<Commit>['finalizeContext']
debug: RequiredOptions<Commit>['debug']
formatDate: RequiredOptions<Commit>['formatDate']
transform: RequiredOptions<Commit>['transform']
commitsSort?: Comparator<Commit>
commitGroupsSort?: Comparator<CommitGroup<Commit>>
Expand Down
20 changes: 6 additions & 14 deletions packages/conventional-changelog-writer/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,17 @@ import type {
StringsRecord
} from './types/index.js'

const utcDateFormatter = Intl.DateTimeFormat('sv-SE', {
timeZone: 'UTC'
})
const DATETIME_LENGTH = 10

/**
* Formats date to yyyy-mm-dd format.
* @param date - Date string, number or Date object.
* @param timeZone - Time zone to use.
* @param date - Date string or Date object.
* @returns Date string in yyyy-mm-dd format.
*/
export function formatDate(date?: string | number | Date, timeZone = 'UTC') {
const dateFormatter = !timeZone || timeZone === 'UTC'
? utcDateFormatter
: Intl.DateTimeFormat('sv-SE', {
timeZone
})

// sv-SEis used for yyyy-mm-dd format
return dateFormatter.format(date ? new Date(date) : new Date())
export function formatDate(
date: string | Date
) {
return new Date(date).toISOString().slice(0, DATETIME_LENGTH)
}

/**
Expand Down
30 changes: 11 additions & 19 deletions packages/conventional-changelog-writer/src/writers.spec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import { describe, it, expect } from 'vitest'
import { delay, throughObj } from '../../../tools/test-tools.js'
import { defaultCommitTransform } from './options.js'
import { formatDate } from './utils.js'
import {
writeChangelogStream,
writeChangelogString
} from './writers.js'

function formatDate(date: Date, timeZone = 'UTC') {
// sv-SE is used for yyyy-mm-dd format
return Intl.DateTimeFormat('sv-SE', {
timeZone
}).format(date)
}

function getTodayDate(timeZone?: string) {
return formatDate(new Date(), timeZone)
}

const todayUtc = getTodayDate()
const todayUtc = formatDate(new Date())
const commits = [
{
hash: '9b1aff905b638aa274a5fc8f88662df446d374bd',
Expand Down Expand Up @@ -249,9 +239,9 @@ describe('conventional-changelog-writer', () => {
it('should merge with the provided transform object', async () => {
let i = 0
const changelog = await writeChangelogString(commits, {}, {
transform(commit) {
transform(commit, context, options) {
return {
...defaultCommitTransform(commit),
...defaultCommitTransform(commit, context, options),
notes: commit.notes.map(note => ({
...note,
title: note.title === 'BREAKING CHANGE'
Expand All @@ -267,9 +257,9 @@ describe('conventional-changelog-writer', () => {
expect(changelog).not.toContain('13f31602f396bc269076ab4d389cfd8ca94b20ba')

for await (let chunk of getStream().pipe(writeChangelogStream({}, {
transform(commit) {
transform(commit, context, options) {
return {
...defaultCommitTransform(commit),
...defaultCommitTransform(commit, context, options),
notes: commit.notes.map(note => ({
...note,
title: note.title === 'BREAKING CHANGE'
Expand Down Expand Up @@ -866,17 +856,19 @@ describe('conventional-changelog-writer', () => {
expect(i).toBe(2)
})

it('should generated date from timeZone of the option', async () => {
it('should generated date using formatDate option', async () => {
let i = 0

for await (const chunk of getStream().pipe(writeChangelogStream({}, {
timeZone: 'America/New_York',
formatDate() {
return 'formatted date'
},
transform() {
return null
}
}))) {
if (i === 0) {
expect(chunk).toBe(`## (${getTodayDate('America/New_York')})\n\n\n\n\n`)
expect(chunk).toBe(`## (formatted date)\n\n\n\n\n`)
}

i++
Expand Down
2 changes: 1 addition & 1 deletion packages/conventional-changelog-writer/src/writers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ async function getRequirements<
options: Options<Commit> = {}
) {
const templates = await loadTemplates(options)
const finalContext = getFinalContext(context, options)
const finalOptions = getFinalOptions(options, templates)
const finalContext = getFinalContext(context, finalOptions)
const generateOn = getGenerateOnFunction(finalContext, finalOptions)
const renderTemplate = createTemplateRenderer(finalContext, finalOptions)

Expand Down

0 comments on commit 8c4bbbe

Please sign in to comment.