1+ import type { GitCommit } from 'tiny-conventional-commits-parser'
12import type { BumpRelease , PromptRelease } from './normalize-options'
23import type { Operation } from './operation'
34import type { ReleaseType } from './release-type'
45import process from 'node:process'
56import c from 'picocolors'
67import prompts from 'prompts'
78import semver , { clean as cleanVersion , valid as isValidVersion , SemVer } from 'semver'
8- import { printRecentCommits } from './print-commits'
99import { isPrerelease , releaseTypes } from './release-type'
1010
1111/**
1212 * Determines the new version number, possibly by prompting the user for it.
1313 */
14- export async function getNewVersion ( operation : Operation ) : Promise < Operation > {
14+ export async function getNewVersion ( operation : Operation , commits : GitCommit [ ] ) : Promise < Operation > {
1515 const { release } = operation . options
1616 const { currentVersion } = operation . state
1717
1818 switch ( release . type ) {
1919 case 'prompt' :
20- return promptForNewVersion ( operation )
20+ return promptForNewVersion ( operation , commits )
2121
2222 case 'version' :
2323 return operation . update ( {
@@ -27,20 +27,27 @@ export async function getNewVersion(operation: Operation): Promise<Operation> {
2727 default :
2828 return operation . update ( {
2929 release : release . type ,
30- newVersion : getNextVersion ( currentVersion , release ) ,
30+ newVersion : getNextVersion ( currentVersion , release , commits ) ,
3131 } )
3232 }
3333}
3434
3535/**
3636 * Returns the next version number of the specified type.
3737 */
38- function getNextVersion ( currentVersion : string , bump : BumpRelease ) : string {
38+ function getNextVersion ( currentVersion : string , bump : BumpRelease , commits : GitCommit [ ] ) : string {
3939 const oldSemVer = new SemVer ( currentVersion )
4040
41- const type = bump . type === 'next'
42- ? oldSemVer . prerelease . length ? 'prerelease' : 'patch'
43- : bump . type
41+ let type : ReleaseType
42+ if ( bump . type === 'next' ) {
43+ type = oldSemVer . prerelease . length ? 'prerelease' : 'patch'
44+ }
45+ else if ( bump . type === 'conventional' ) {
46+ type = oldSemVer . prerelease . length ? 'prerelease' : determineSemverChange ( commits )
47+ }
48+ else {
49+ type = bump . type
50+ }
4451
4552 const newSemVer = oldSemVer . inc ( type , bump . preid )
4653
@@ -61,18 +68,32 @@ function getNextVersion(currentVersion: string, bump: BumpRelease): string {
6168 return newSemVer . version
6269}
6370
71+ function determineSemverChange ( commits : GitCommit [ ] ) {
72+ let [ hasMajor , hasMinor ] = [ false , false ]
73+ for ( const commit of commits ) {
74+ if ( commit . isBreaking ) {
75+ hasMajor = true
76+ }
77+ else if ( commit . type === 'feat' ) {
78+ hasMinor = true
79+ }
80+ }
81+
82+ return hasMajor ? 'major' : hasMinor ? 'minor' : 'patch'
83+ }
84+
6485/**
6586 * Returns the next version number for all release types.
6687 */
67- function getNextVersions ( currentVersion : string , preid : string ) : Record < ReleaseType , string > {
88+ function getNextVersions ( currentVersion : string , preid : string , commits : GitCommit [ ] ) : Record < ReleaseType , string > {
6889 const next : Record < string , string > = { }
6990
7091 const parse = semver . parse ( currentVersion )
7192 if ( typeof parse ?. prerelease [ 0 ] === 'string' )
7293 preid = parse ?. prerelease [ 0 ] || 'preid'
7394
7495 for ( const type of releaseTypes )
75- next [ type ] = getNextVersion ( currentVersion , { type, preid } )
96+ next [ type ] = getNextVersion ( currentVersion , { type, preid } , commits )
7697
7798 return next
7899}
@@ -82,17 +103,13 @@ function getNextVersions(currentVersion: string, preid: string): Record<ReleaseT
82103 *
83104 * @returns - A tuple containing the new version number and the release type (if any)
84105 */
85- async function promptForNewVersion ( operation : Operation ) : Promise < Operation > {
106+ async function promptForNewVersion ( operation : Operation , commits : GitCommit [ ] ) : Promise < Operation > {
86107 const { currentVersion } = operation . state
87108 const release = operation . options . release as PromptRelease
88109
89- const next = getNextVersions ( currentVersion , release . preid )
110+ const next = getNextVersions ( currentVersion , release . preid , commits )
90111 const configCustomVersion = await operation . options . customVersion ?.( currentVersion , semver )
91112
92- if ( operation . options . printCommits ) {
93- await printRecentCommits ( operation )
94- }
95-
96113 const PADDING = 13
97114 const answers = await prompts ( [
98115 {
@@ -105,6 +122,7 @@ async function promptForNewVersion(operation: Operation): Promise<Operation> {
105122 { value : 'minor' , title : `${ 'minor' . padStart ( PADDING , ' ' ) } ${ c . bold ( next . minor ) } ` } ,
106123 { value : 'patch' , title : `${ 'patch' . padStart ( PADDING , ' ' ) } ${ c . bold ( next . patch ) } ` } ,
107124 { value : 'next' , title : `${ 'next' . padStart ( PADDING , ' ' ) } ${ c . bold ( next . next ) } ` } ,
125+ { value : 'conventional' , title : `${ 'conventional' . padStart ( PADDING , ' ' ) } ${ c . bold ( next . conventional ) } ` } ,
108126 ...configCustomVersion
109127 ? [
110128 { value : 'config' , title : `${ 'from config' . padStart ( PADDING , ' ' ) } ${ c . bold ( configCustomVersion ) } ` } ,
@@ -146,6 +164,7 @@ async function promptForNewVersion(operation: Operation): Promise<Operation> {
146164 case 'custom' :
147165 case 'config' :
148166 case 'next' :
167+ case 'conventional' :
149168 case 'none' :
150169 return operation . update ( { newVersion } )
151170
0 commit comments