relion 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -32,10 +32,9 @@ interface TransformedConfig extends Omit<MergedConfig, 'changelog'> {
32
32
  bump: FalseOrComplete<VersionedFile[]>;
33
33
  changelog: false | ResolvedChangelogOptions;
34
34
  }
35
- interface ContextualConfig extends TransformedConfig {
35
+ interface ResolvedConfig extends TransformedConfig {
36
36
  context: ResolvedContext;
37
37
  }
38
- type ResolvedConfig = ContextualConfig;
39
38
  type BumpFiles = (string | VersionedFile)[];
40
39
  interface ChangelogOptions {
41
40
  output?: 'stdout' | (string & {});
@@ -156,11 +155,10 @@ interface ParsedCommit extends CommitMessage {
156
155
  associatedReleaseTag?: string;
157
156
  isReverted?: 'inTheSameRelease' | 'inOtherRelease' | null;
158
157
  }
159
- interface ParsedCommitWithReleaseTag extends ParsedCommit {
158
+ interface ResolvedCommit extends ParsedCommit {
160
159
  associatedReleaseTag: NonNullable<ParsedCommit['associatedReleaseTag']>;
161
- }
162
- interface ResolvedCommit extends ParsedCommitWithReleaseTag {
163
160
  isReverted: NonNullable<ParsedCommit['isReverted']> | null;
161
+ breakingChangeIndex?: number;
164
162
  }
165
163
  interface CommitMessage {
166
164
  type: string;
@@ -285,8 +283,7 @@ interface ReleaseWithTypeGroups extends Omit<ReleaseWithFlatCommits, 'commits'>
285
283
  commitTypeGroups: FilledTypeGroupMap;
286
284
  }
287
285
  interface ReleaseContext extends ReleaseWithTypeGroups, ResolvedContext {
288
- prevTag?: string;
289
- prevVersion?: string;
286
+ prevRelease: Partial<ReleaseWithTypeGroups>;
290
287
  }
291
288
  type DefaultChangelogSections = typeof defaultChangelogSections;
292
289
  interface ChangelogSectionsSelector extends DefaultChangelogSections {
@@ -301,4 +298,4 @@ declare const defineConfig: (config: UserConfig) => UserConfig;
301
298
  //#region src/utils/sections-selector.d.ts
302
299
  declare const changelogSectionsSelector: ChangelogSectionsSelector;
303
300
  //#endregion
304
- export { BumpFiles, ChangelogOptions, ChangelogSectionsSelector, CommitMessage, CommitOptions, CommitRange, CommitsParser, CompleteChangelogOptions, CompleteCommitOptions, CompleteCommitsParser, CompleteTagOptions, Context, ContextualConfig, Contributor, DefaultChangelogSections, DefaultVersionedFile, FalseOrComplete, FilledTypeGroup, FilledTypeGroupMap, GithubUserInfo, GpgSig, GpgSigCode, MergedConfig, ParsedCommit, RawCommit, RawReference, RefLabel, Reference, ReleaseContext, ReleaseType, ReleaseWithFlatCommits, ReleaseWithTypeGroups, RepoInfo, ResolvedChangelogOptions, ResolvedCommit, ResolvedConfig, ResolvedContext, TagOptions, TransformedConfig, TypeGroupDefinition, TypeGroupsMap, UserConfig, VersionedFile, changelogSectionsSelector, relion as default, defineConfig };
301
+ export { BumpFiles, ChangelogOptions, ChangelogSectionsSelector, CommitMessage, CommitOptions, CommitRange, CommitsParser, CompleteChangelogOptions, CompleteCommitOptions, CompleteCommitsParser, CompleteTagOptions, Context, Contributor, DefaultChangelogSections, DefaultVersionedFile, FalseOrComplete, FilledTypeGroup, FilledTypeGroupMap, GithubUserInfo, GpgSig, GpgSigCode, MergedConfig, ParsedCommit, RawCommit, RawReference, RefLabel, Reference, ReleaseContext, ReleaseType, ReleaseWithFlatCommits, ReleaseWithTypeGroups, RepoInfo, ResolvedChangelogOptions, ResolvedCommit, ResolvedConfig, ResolvedContext, TagOptions, TransformedConfig, TypeGroupDefinition, TypeGroupsMap, UserConfig, VersionedFile, changelogSectionsSelector, relion as default, defineConfig };
package/dist/index.js CHANGED
@@ -1,46 +1,56 @@
1
- import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import r from"handlebars";import i from"semver";import{createHash as a}from"node:crypto";import{execSync as o}from"node:child_process";const s={bump:!1,changelog:!1,commit:!1,tag:!1,versionSourceFile:`./package.json`,newTagFormat:`v{{newVersion}}`,prevReleaseTagPattern:/^v?(?<version>\d+\.\d+\.\d+)/,zeroMajorBreakingIsMinor:!0,dryRun:!1,silent:!1,context:{commitHyperlink:!0,refHyperlink:!0},commitsParser:{headerPattern:/^(?<type>\w+)(?:\((?<scope>.+)\))?(?<bang>!)?: (?<subject>.+)/s,breakingChangesPattern:/BREAKING CHANGES?:\s*(?<content>.+)/s,breakingChangeListPattern:/- (.+)/g,tagPattern:/tag: (?<tag>.*?)[,)]/g,coAuthorPattern:/Co-authored-by: (?<name>.+?) <(?<email>.+)>/g,signerPattern:/Signed-off-by: (?<name>.+?) <(?<email>.+)>/g,ghEmailPattern:/^(?:\d+\+)?(?<username>.+)@users\.noreply\.github\.com$/,remoteUrlPattern:/^(https:\/\/|git@)(?<host>[^/:]+)[/:](?<owner>.+?)\/(?<name>.+?)(?:\..*)?$/,refPattern:/^(?<action>.+?) (?<labels>.+)$/gm,refLabelPattern:/(?:(?<owner>\S+?)\/(?<repo>\S+?))?#(?<number>\d+)/g,refActionPattern:/Fixes|Closes|Refs/i,dateSource:`authorDate`,dateFormat:`US`,revertCommitBodyPattern:/^This reverts commit (?<hash>.{7})\./m}},c={breaking:{title:`⚠️ BREAKING CHANGES`,commitType:`breaking`},feat:{title:`✨ Features`,commitType:`feat`},fix:{title:`🩹 Fixes`,commitType:`fix`},perf:{title:`⚡ Performance`,commitType:`perf`},refactor:{title:`🚜 Refactoring`,commitType:`refactor`},docs:{title:`📚 Documentation`,commitType:`docs`},style:{title:`🎨 Style`,commitType:`style`},build:{title:`📦 Build`,commitType:`build`},ci:{title:`🚀 CI`,commitType:`ci`},revert:{title:`♻️ Reverts`,commitType:`revert`},types:{title:`🏷️ Types`,commitType:`types`},deps:{title:`🧩 Dependencies`,commitType:`chore`,filter:e=>!!e.scope?.includes(`deps`)},chore:{title:`🛠️ Chores`,commitType:`chore`},test:{title:`🧪 Tests`,commitType:`test`},misc:{title:`⚙️ Miscellaneous`,commitType:`*`,filter:e=>e.type!==`release`}},l={output:`CHANGELOG.md`,commitRange:`unreleased`,sections:c,header:`# Changelog
1
+ import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import r from"handlebars";import i from"semver";import{createHash as a}from"node:crypto";import{execSync as o}from"node:child_process";const s={bump:!1,changelog:!1,commit:!1,tag:!1,versionSourceFile:`./package.json`,newTagFormat:`v{{version}}`,prevReleaseTagPattern:/^v?(?<version>\d+\.\d+\.\d+)/,zeroMajorBreakingIsMinor:!0,dryRun:!1,silent:!1,context:{commitHyperlink:!0,refHyperlink:!0},commitsParser:{headerPattern:/^(?<type>\w+)(?:\((?<scope>.+)\))?(?<bang>!)?: (?<subject>.+)/s,breakingChangesPattern:/BREAKING CHANGES?:\s*(?<content>.+)/s,breakingChangeListPattern:/- (.+)/g,tagPattern:/tag: (?<tag>.*?)[,)]/g,coAuthorPattern:/Co-authored-by: (?<name>.+?) <(?<email>.+)>/g,signerPattern:/Signed-off-by: (?<name>.+?) <(?<email>.+)>/g,ghEmailPattern:/^(?:\d+\+)?(?<username>.+)@users\.noreply\.github\.com$/,remoteUrlPattern:/^(https:\/\/|git@)(?<host>[^/:]+)[/:](?<owner>.+?)\/(?<name>.+?)(?:\..*)?$/,refPattern:/^(?<action>.+?) (?<labels>.+)$/gm,refLabelPattern:/(?:(?<owner>\S+?)\/(?<repo>\S+?))?#(?<number>\d+)/g,refActionPattern:/Fixes|Closes|Refs/i,dateSource:`authorDate`,dateFormat:`US`,revertCommitBodyPattern:/^This reverts commit (?<hash>.{7})/m}},c={breaking:{title:`⚠️ BREAKING CHANGES`,commitType:`breaking`},feat:{title:`✨ Features`,commitType:`feat`},fix:{title:`🩹 Fixes`,commitType:`fix`},perf:{title:`⚡ Performance`,commitType:`perf`},refactor:{title:`🚜 Refactoring`,commitType:`refactor`},docs:{title:`📚 Documentation`,commitType:`docs`},style:{title:`🎨 Style`,commitType:`style`},build:{title:`📦 Build`,commitType:`build`},ci:{title:`🚀 CI`,commitType:`ci`},revert:{title:`♻️ Reverts`,commitType:`revert`},types:{title:`🏷️ Types`,commitType:`types`},deps:{title:`🧩 Dependencies`,commitType:`chore`,filter:e=>!!e.scope?.includes(`deps`)},chore:{title:`🛠️ Chores`,commitType:`chore`},test:{title:`🧪 Tests`,commitType:`test`},misc:{title:`⚙️ Miscellaneous`,commitType:`*`,filter:e=>e.type!==`release`}},l={output:`CHANGELOG.md`,commitRange:`unreleased`,sections:c,header:`# Changelog
2
2
 
3
3
 
4
- `,prevReleaseHeaderPattern:/^##.*?\d+\.\d+\.\d+/m,helpers:{eq:(e,t)=>e===t,repeat:(e,t)=>e.repeat(t),isArray:e=>Array.isArray(e)},partials:{}},u={message:`release({{repo.name}}): {{newTag}}`,signOff:!1,gpgSign:!1,stageAll:!0,extraArgs:``},d={name:`{{newTag}}`,message:`release({{repo.name}}): {{newTag}}`,gpgSign:!1,force:!1,extraArgs:``},f=[{filePathRegex:/package\.json$/,versionPattern:/(^.*?"version".*?")(.*?)(")/s},{filePathRegex:/package-lock\.json$/,versionPattern:/(^.*?"version".*?"|"packages".*?"".*"version".*?")(.*?)(")/gs}],p=async e=>{let t=m(e),n=h(t),r=g(n),i=await _(r),a=S(i);return a},m=e=>{let t=e.profile;if(!t)return e;let n=e[`_${t}`];if(!n)throw Error(`Profile "${t}" not found in configuration.`);let r=(t,...r)=>{let i=e=>Object.prototype.toString.call(e)===`[object Object]`,a=(e,t)=>{let n=i(e),r=i(t);if(n&&r)return{...e,...t};if(!n&&r)return t;if(n&&!r)return e},o=e[t],s=n[t],c=a(o,s);if(c!==void 0)return r.forEach(e=>{c[e]=a(o?.[e],s?.[e])}),c};return{...e,...n,commitsParser:r(`commitsParser`),changelog:r(`changelog`,`partials`,`helpers`),commit:r(`commit`),tag:r(`tag`),context:r(`context`)}},h=e=>{let t=(e,t,n,...r)=>{if(t==null||t===!1)return!1;if(t===!0)return n;if(typeof t!=`object`)throw Error(`Invalid value for ${e}. It should be a boolean or an object.`);let i={...n,...t};return r.forEach(e=>i[e]={...n[e],...t[e]}),i};return{...s,...e,commitsParser:{...s.commitsParser,...e.commitsParser},changelog:t(`changelog`,e.changelog,l,`partials`,`helpers`),commit:t(`commit`,e.commit,u),tag:t(`tag`,e.tag,d)}},g=e=>{let t=e=>{let t=f.find(t=>t.filePathRegex.test(e));if(t)return{filePath:e,versionPattern:t.versionPattern};throw Error(`File ${e} doesn't match any default versioned files. Please provide a custom version pattern for this file.`)},n=e=>{if(e===!1)return!1;if(e===!0)return[i];if(Array.isArray(e))return[i,...e.map(e=>typeof e==`string`?t(e):e)];throw Error(`Invalid value for bump. It should be a boolean or an array.`)},i=typeof e.versionSourceFile==`string`?t(e.versionSourceFile):e.versionSourceFile;return{...e,versionSourceFile:i,bump:n(e.bump),changelog:e.changelog===!1?!1:{...e.changelog,compiledPartials:Object.fromEntries(Object.entries(e.changelog.partials).map(([e,t])=>[e,r.compile(t)]))}}},_=async e=>{let t=e.context??{},n=B(e.commitsParser.remoteUrlPattern);t.repo={...n,...t.repo};let r=e.changelog?e.changelog.commitRange:`unreleased`,i=e.context?.commits?(await Promise.all(e.context.commits.map(async t=>typeof t==`object`&&`message`in t||typeof t==`string`?await M(t,e.commitsParser,e.prevReleaseTagPattern):t))).filter(e=>e!=null):await j(r,e.commitsParser,e.prevReleaseTagPattern);t.currentVersion??=w(e.versionSourceFile),t.currentTag??=U(e.prevReleaseTagPattern)[0],t.newVersion??=await T(e,t.currentVersion),t.newTag??=C(e.newTagFormat,t),t.commits=v(i,t.newTag,e.commitsParser.revertCommitBodyPattern);let a={...e,context:t};return t.releases=e.changelog?y(t.commits,e.changelog.sections,a):null,a},v=(e,t,n)=>{let r=(e,t)=>e.map(e=>({...e,associatedReleaseTag:e.associatedReleaseTag??t})),i=e=>e.map(t=>{let r=null,i=e.find(e=>e.type===`revert`&&n.exec(e.body??``)?.groups?.hash===t.hash);return i&&(r=i.associatedReleaseTag===t.associatedReleaseTag?`inTheSameRelease`:`inOtherRelease`),{...t,isReverted:t.isReverted??r}}),a=r(e,t),o=i(a);return o},y=(e,t,n)=>{let r={};return e.forEach(e=>{let t=e.associatedReleaseTag;t in r?r[t].commits.push(e):r[t]={tag:t,version:n.prevReleaseTagPattern.exec(t)?.groups?.version,date:e.date,commits:[e]}}),Object.values(r).map(e=>b(e,t)).filter(e=>Object.keys(e.commitTypeGroups).length)},b=(e,t)=>{let{commits:n,...r}=e;return{...r,commitTypeGroups:x(n,t)}},x=(e,t)=>{let n=Object.fromEntries(Object.entries(t).map(([e,t])=>[e,{...t,commits:[]}]));return e.forEach(e=>{let r=!!e.breakingChanges,i=!1,a=!1;for(let o in t){if(t[o].filter&&!t[o].filter(e))continue;let s=[t[o].commitType].flat();if(r&&!a&&s.includes(`breaking`)){n[o].commits.push(e),a=!0;continue}if(!i&&(s.includes(e.type)||s.includes(`*`))&&(n[o].commits.push(e),i=!0),i&&(!r||a))return}}),Object.keys(n).forEach(e=>{n[e].commits.length||delete n[e]}),n},S=e=>({...e,commit:e.commit?{...e.commit,message:C(e.commit.message,e.context)}:e.commit,tag:e.tag?{...e.tag,name:C(e.tag.name,e.context),message:C(e.tag.message,e.context)}:e.tag}),C=(e,t)=>{let n=r.compile(e);return n(t)},w=e=>{let n=t(e.filePath,`utf8`),r=e.versionPattern.exec(n)?.[2];if(!r)throw Error(`Version not found in '${e.filePath}' with pattern '${e.versionPattern}'`);if(!i.valid(r))throw Error(`Invalid version format in '${e.filePath}': '${r}'`);return K(`Current version from '${e.filePath}': '${r}'`),r},T=async(e,t)=>{if(e.releaseVersion){if(!i.valid(e.releaseVersion))throw Error(`Invalid release version format: '${e.releaseVersion}'`);return e.releaseVersion}let n;if(e.releaseType)n=e.releaseType;else{let r=await j(`unreleased`,e.commitsParser,e.prevReleaseTagPattern);n=E(r),e.zeroMajorBreakingIsMinor&&i.major(t)===0&&n===`major`&&(n=`minor`)}let r=D(t,n);return K(`Determined new version: '${r}' (release type: '${n}')`),r},E=e=>{let t=e.some(e=>e.breakingChanges);if(t)return`major`;let n=e.some(e=>e.type===`feat`);return n?`minor`:`patch`},D=(e,t)=>i.inc(e,t)??(()=>{throw Error(`Failed to calculate new version from '${e}' with release type '${t}'`)})();let O=function(e){return e.G=`valid`,e.B=`bad`,e.U=`valid, unknown validity`,e.X=`valid, expired`,e.Y=`valid, made by expired key`,e.R=`valid, made by revoked key`,e.E=`cannot check (missing key)`,e.N=`no signature`,e}({});const k={};let A;const j=async(e,t,n)=>{let r=Array.isArray(e)?e:V(e,n),i=t,a=(await Promise.all(r.map(async e=>M(e,i,n)))).filter(e=>e!==null);return A=void 0,a},M=async(e,t,n)=>{typeof e==`string`&&(e={message:e});let{tagRefs:r,hash:i=N(e.message)}=e;if(i in k)return k[i];let a=e.message.trim();if(!a)throw Error(`Message is missing for commit: ${JSON.stringify(e)}`);let o;try{o=P(a,t)}catch(e){return console.warn(`Error parsing commit '${i}':`,e.message),null}let{type:s,scope:c,subject:l,body:u,breakingChanges:d,footer:f}=o,p=r?[...r.matchAll(t.tagPattern)].map(e=>e.groups?.tag??``):[],m=f?[...f.matchAll(t.signerPattern)].map(e=>e.groups):[],h=[],g=e=>{h.some(t=>t.email===e.email)||h.push(e)},_=e.authorName&&e.authorEmail?I({name:e.authorName,email:e.authorEmail},m):void 0;_&&g(_);let v=e.committerName&&e.committerEmail?I({name:e.committerName,email:e.committerEmail},m):void 0;v&&g(v);let y=f?[...f.matchAll(t.coAuthorPattern)].map(e=>e.groups).map(e=>I(e,m)):[];y.forEach(e=>g(e));let b=await L(f??``,t),x=e.gpgSigCode?{code:e.gpgSigCode,label:O[e.gpgSigCode],keyId:e.gpgSigKeyId}:void 0,S=e[t.dateSource===`committerDate`?`committerTs`:`authorTs`];typeof S==`string`&&(S=R(new Date(S*1e3),t.dateFormat));let C=p.find(e=>n.exec(e)),w=C??A;w&&(A=w);let T={hash:i,type:s,scope:c,subject:l,body:u,breakingChanges:d,footer:f,committer:v,gpgSig:x,date:S,releaseTag:C,associatedReleaseTag:w,tags:p.length?p:void 0,authors:h.length?h:void 0,refs:b.length?b:void 0};return i&&!(i in k)&&(k[i]=T),T},N=e=>`fake_`+a(`sha256`).update(e,`utf8`).digest(`hex`).slice(0,7),P=(e,t)=>{let[n,...r]=e.split(`
4
+ `,prevReleaseHeaderPattern:/^##.*?\d+\.\d+\.\d+/m,helpers:{eq:(e,t)=>e===t,repeat:(e,t)=>e.repeat(t),isArray:e=>Array.isArray(e),isBreakingCommitInOtherTypeGroup:(e,t)=>Object.entries(t.data.root.commitTypeGroups).filter(([,{commitType:e}])=>e!==`breaking`).some(([,t])=>t.commits.includes(e))},partials:{}},u={message:`release({{repo.name}}): {{newTag}}`,signOff:!1,gpgSign:!1,stageAll:!0,extraArgs:``},d={name:`{{newTag}}`,message:`release({{repo.name}}): {{newTag}}`,gpgSign:!1,force:!1,extraArgs:``},f=[{filePathRegex:/package\.json$/,versionPattern:/(^.*?"version".*?")(.*?)(")/s},{filePathRegex:/package-lock\.json$/,versionPattern:/(^.*?"version".*?"|"packages".*?"".*"version".*?")(.*?)(")/gs}],p=async e=>{let t=m(e),n=h(t),r=g(n),i=await _(r),a=x(i);return a},m=e=>{let t=e.profile;if(!t)return e;let n=e[`_${t}`];if(!n)throw Error(`Profile "${t}" not found in configuration.`);let r=(t,...r)=>{let i=e=>Object.prototype.toString.call(e)===`[object Object]`,a=(e,t)=>{let n=i(e),r=i(t);if(n&&r)return{...e,...t};if(!n&&r)return t;if(n&&!r)return e},o=e[t],s=n[t],c=a(o,s);if(c!==void 0)return r.forEach(e=>{c[e]=a(o?.[e],s?.[e])}),c};return{...e,...n,commitsParser:r(`commitsParser`),changelog:r(`changelog`,`partials`,`helpers`),commit:r(`commit`),tag:r(`tag`),context:r(`context`)}},h=e=>{let t=(e,t,n,...r)=>{if(t==null||t===!1)return!1;if(t===!0)return n;if(typeof t!=`object`)throw Error(`Invalid value for ${e}. It should be a boolean or an object.`);let i={...n,...t};return r.forEach(e=>i[e]={...n[e],...t[e]}),i};return{...s,...e,commitsParser:{...s.commitsParser,...e.commitsParser},changelog:t(`changelog`,e.changelog,l,`partials`,`helpers`),commit:t(`commit`,e.commit,u),tag:t(`tag`,e.tag,d)}},g=e=>{let t=e=>{let t=f.find(t=>t.filePathRegex.test(e));if(t)return{filePath:e,versionPattern:t.versionPattern};throw Error(`File ${e} doesn't match any default versioned files. Please provide a custom version pattern for this file.`)},n=e=>{if(e===!1)return!1;if(e===!0)return[i];if(Array.isArray(e))return[i,...e.map(e=>typeof e==`string`?t(e):e)];throw Error(`Invalid value for bump. It should be a boolean or an array.`)},i=typeof e.versionSourceFile==`string`?t(e.versionSourceFile):e.versionSourceFile;return{...e,versionSourceFile:i,bump:n(e.bump),changelog:e.changelog===!1?!1:{...e.changelog,compiledPartials:Object.fromEntries(Object.entries(e.changelog.partials).map(([e,t])=>[e,r.compile(t)]))}}},_=async e=>{let t=z(e.commitsParser.remoteUrlPattern),n=S(e.versionSourceFile),r=H(e.prevReleaseTagPattern)[0],i=await w(e,n),a=e.newTagFormat.replace(`{{version}}`,i),o=e.changelog?e.changelog.commitRange:`unreleased`,s=e.context?.commits?(await Promise.all(e.context.commits.map(async t=>typeof t==`object`&&`message`in t||typeof t==`string`?await j(t,e.commitsParser,e.prevReleaseTagPattern):t))).filter(e=>e!=null):await A(o,e.commitsParser,e.prevReleaseTagPattern),c=v(s,a,e.commitsParser.revertCommitBodyPattern),l=e.changelog?y(c,e.changelog.sections,e.prevReleaseTagPattern):null,{commits:u,...d}=e.context??{};return{...e,context:{currentVersion:n,currentTag:r,newVersion:i,newTag:a,commits:c,releases:l,...d,repo:{...t,...e.context?.repo}}}},v=(e,t,n)=>{let r=0;return e.map(i=>{let a=null,o=e.find(e=>e.type===`revert`&&n.exec(e.body??``)?.groups?.hash===i.hash);return o&&(a=o.associatedReleaseTag===i.associatedReleaseTag?`inTheSameRelease`:`inOtherRelease`),{...i,associatedReleaseTag:i.associatedReleaseTag??t,isReverted:i.isReverted??a,breakingChangeIndex:i.breakingChanges?++r:void 0}})},y=(e,t,n)=>{let r={};return e.forEach(e=>{let t=e.associatedReleaseTag;t in r?r[t].commits.push(e):r[t]={tag:t,version:C(t,n),date:e.date,commits:[e]}}),Object.values(r).map(({commits:e,...n})=>({...n,commitTypeGroups:b(e,t)})).filter(e=>Object.keys(e.commitTypeGroups).length)},b=(e,t)=>{let n=Object.fromEntries(Object.entries(t).map(([e,{filter:t,...n}])=>[e,{...n,commits:[]}]));return e.forEach(e=>{let r=!!e.breakingChanges,i=!1,a=!1;for(let o in t){if(t[o].filter&&!t[o].filter(e))continue;let s=[t[o].commitType].flat();if(r&&!a&&s.includes(`breaking`)){n[o].commits.push(e),a=!0;continue}if(!i&&(s.includes(e.type)||s.includes(`*`))&&(n[o].commits.push(e),i=!0),i&&(!r||a))return}}),Object.fromEntries(Object.entries(n).filter(([e,t])=>t.commits.length))},x=e=>({...e,commit:e.commit?{...e.commit,message:q(e.commit.message,e.context)}:e.commit,tag:e.tag?{...e.tag,name:q(e.tag.name,e.context),message:q(e.tag.message,e.context)}:e.tag}),S=e=>{let n=t(e.filePath,`utf8`),r=e.versionPattern.exec(n)?.[2];if(!r)throw Error(`Version not found in '${e.filePath}' with pattern '${e.versionPattern}'`);if(!i.valid(r))throw Error(`Invalid version format in '${e.filePath}': '${r}'`);return G(`Current version from '${e.filePath}': '${r}'`),r},C=(e,t)=>t.exec(e)?.groups?.version,w=async(e,t)=>{if(e.releaseVersion){if(!i.valid(e.releaseVersion))throw Error(`Invalid release version format: '${e.releaseVersion}'`);return e.releaseVersion}let n;if(e.releaseType)n=e.releaseType;else{let r=await A(`unreleased`,e.commitsParser,e.prevReleaseTagPattern);n=T(r),e.zeroMajorBreakingIsMinor&&i.major(t)===0&&n===`major`&&(n=`minor`)}let r=E(t,n);return G(`Determined new version: '${r}' (release type: '${n}')`),r},T=e=>{let t=e.some(e=>e.breakingChanges);if(t)return`major`;let n=e.some(e=>e.type===`feat`);return n?`minor`:`patch`},E=(e,t)=>i.inc(e,t)??(()=>{throw Error(`Failed to calculate new version from '${e}' with release type '${t}'`)})();let D=function(e){return e.G=`valid`,e.B=`bad`,e.U=`valid, unknown validity`,e.X=`valid, expired`,e.Y=`valid, made by expired key`,e.R=`valid, made by revoked key`,e.E=`cannot check (missing key)`,e.N=`no signature`,e}({});const O={};let k;const A=async(e,t,n)=>{let r=Array.isArray(e)?e:B(e,n),i=t,a=(await Promise.all(r.map(async e=>j(e,i,n)))).filter(e=>e!==null);return k=void 0,a},j=async(e,t,n)=>{typeof e==`string`&&(e={message:e});let{tagRefs:r,hash:i=M(e.message)}=e;if(i in O)return O[i];let a=e.message.trim();if(!a)throw Error(`Message is missing for commit: ${JSON.stringify(e)}`);let o;try{o=N(a,t)}catch(e){return console.warn(`Error parsing commit '${i}':`,e.message),null}let{type:s,scope:c,subject:l,body:u,breakingChanges:d,footer:f}=o,p=r?[...r.matchAll(t.tagPattern)].map(e=>e.groups?.tag??``):[],m=f?[...f.matchAll(t.signerPattern)].map(e=>e.groups):[],h=[],g=e=>{h.some(t=>t.email===e.email)||h.push(e)},_=e.authorName&&e.authorEmail?F({name:e.authorName,email:e.authorEmail},m):void 0;_&&g(_);let v=e.committerName&&e.committerEmail?F({name:e.committerName,email:e.committerEmail},m):void 0;v&&g(v);let y=f?[...f.matchAll(t.coAuthorPattern)].map(e=>e.groups).map(e=>F(e,m)):[];y.forEach(e=>g(e));let b=await I(f??``,t),x=e.gpgSigCode?{code:e.gpgSigCode,label:D[e.gpgSigCode],keyId:e.gpgSigKeyId}:void 0,S=e[t.dateSource===`committerDate`?`committerTs`:`authorTs`];typeof S==`string`&&(S=L(new Date(S*1e3),t.dateFormat));let C=p.find(e=>n.exec(e)),w=C??k;w&&(k=w);let T={hash:i,type:s,scope:c,subject:l,body:u,breakingChanges:d,footer:f,committer:v,gpgSig:x,date:S,releaseTag:C,associatedReleaseTag:w,tags:p.length?p:void 0,authors:h.length?h:void 0,refs:b.length?b:void 0};return i&&!(i in O)&&(O[i]=T),T},M=e=>`fake_`+a(`sha256`).update(e,`utf8`).digest(`hex`).slice(0,7),N=(e,t)=>{let[n,...r]=e.split(`
5
5
 
6
- `),i=t.headerPattern.exec(n);if(!i?.groups)throw Error(`Commit header '${n}' doesn't match expected format`);let{type:a,scope:o,bang:s,subject:c}=i.groups,l,u=r.find(e=>t.breakingChangesPattern.test(e));u?(l=F(u,t),r.splice(r.indexOf(u),1)):s&&(l=c);let d=r.findIndex(e=>e.match(t.refActionPattern)??e.match(t.coAuthorPattern)??e.match(t.signerPattern)),[f,p]=d===-1?[r.join(`
6
+ `),i=t.headerPattern.exec(n);if(!i?.groups)throw Error(`Commit header '${n}' doesn't match expected format`);let{type:a,scope:o,bang:s,subject:c}=i.groups,l,u=r.find(e=>t.breakingChangesPattern.test(e));u?(l=P(u,t),r.splice(r.indexOf(u),1)):s&&(l=c);let d=r.findIndex(e=>e.match(t.refActionPattern)??e.match(t.coAuthorPattern)??e.match(t.signerPattern)),[f,p]=d===-1?[r.join(`
7
7
 
8
8
  `),``]:[r.slice(0,d).join(`
9
9
 
10
10
  `),r.slice(d).join(`
11
11
 
12
- `)];return{type:a,scope:o||void 0,subject:c,body:f||void 0,breakingChanges:l,footer:p||void 0}},F=(e,t)=>{let n=t.breakingChangesPattern.exec(e)?.groups?.content;if(!n)throw Error(`Failed to extract breaking changes content from '${e}' using pattern "${t.breakingChangesPattern}"`);let r=[...n.matchAll(t.breakingChangeListPattern)];return r.length?r.map(e=>e[1]):n},I=(e,t)=>{let n=t.some(t=>t.email===e.email&&t.name===e.name);return{...e,hasSignedOff:n,ghLogin:e.name,ghUrl:`https://github.com/${e.name}`}},L=async(e,t)=>await Promise.all([...e.matchAll(t.refPattern)].map(e=>e.groups).filter(e=>t.refActionPattern.test(e.action)).flatMap(e=>[...e.labels.matchAll(t.refLabelPattern)].map(e=>e.groups).filter(e=>!!e.number).map(t=>({action:e.action,owner:t.owner,repo:t.repo,number:t.number})))),R=(e,t)=>{let n=e=>e.toString().padStart(2,`0`);if(t===`US`)return e.toLocaleString(`en-US`,{month:`short`,day:`numeric`,year:`numeric`});if(t===`ISO`)return e.toISOString().split(`T`)[0];let r={YYYY:e.getUTCFullYear().toString(),MM:n(e.getUTCMonth()+1),DD:n(e.getUTCDate()),HH:n(e.getUTCHours()),mm:n(e.getUTCMinutes()),ss:n(e.getUTCSeconds())};return t.replace(/YYYY|MM|DD|HH|mm|ss/g,e=>r[e])},z=/##COMMIT##\n#HASH# (?<hash>.+)?\n#MSG# (?<message>[\s\S]*?)\n#REFS#\s+(?<tagRefs>.+)?\n#AUTHOR-NAME# (?<authorName>.+)?\n#AUTHOR-EMAIL# (?<authorEmail>.+)?\n#AUTHOR-DATE# (?<authorTs>.+)?\n#COMMITTER-NAME# (?<committerName>.+)?\n#COMMITTER-EMAIL# (?<committerEmail>.+)?\n#COMMITTER-DATE# (?<committerTs>.+)?\n#GPGSIG-CODE# (?<gpgSigCode>.+)?\n#GPGSIG-KEYID# (?<gpgSigKeyId>.+)?/g,B=e=>{let t=o(`git remote get-url origin`,{encoding:`utf8`}).trim(),n=e.exec(t);if(!n?.groups)throw Error(`Couldn't parse remote URL: `+t);let{host:r,owner:i,name:a}=n.groups,s=`https://${r}/${i}/${a}`;return{host:r,owner:i,name:a,homepage:s}},V=(e,t)=>{let n=H(),r=U(t),i=``,a=``,s=``;if(e===`all`)i=`{{firstCommit}}`,a=`HEAD`;else if(e===`unreleased`)i=r[0]??`{{firstCommit}}`,a=`HEAD`;else if(e===`latest-release`)i=r[1]??`{{firstCommit}}`,a=r[0]??`HEAD`;else if(typeof e==`object`){let t=r.indexOf(e.versionTag);if(t===-1)throw Error(`Version tag '${e.versionTag}' not found`);i=r[t+1]??`{{firstCommit}}`,a=r[t]}else s=e;i&&a&&(s=i===`{{firstCommit}}`?`"${i}^!" ${a}`:`"${i}..${a}"`),s=s.replace(`{{firstCommit}}`,n);let c=o(`git log ${s} --pretty="##COMMIT##%n#HASH# %h%n#MSG# %B%n#REFS# %d%n#AUTHOR-NAME# %an%n#AUTHOR-EMAIL# %ae%n#AUTHOR-DATE# %at%n#COMMITTER-NAME# %cn%n#COMMITTER-EMAIL# %ce%n#COMMITTER-DATE# %ct%n#GPGSIG-CODE# %G?%n#GPGSIG-KEYID# %GK%n"`,{encoding:`utf8`});return[...c.matchAll(z)].map(e=>e.groups)},H=()=>o(`git rev-list --max-parents=0 HEAD`,{encoding:`utf8`}).trim(),U=e=>{let t=o(`git tag --sort=-creatordate`,{encoding:`utf8`});return t.split(`
13
- `).filter(t=>e.test(t))};let W=!1;const G=e=>W=e,K=(...e)=>{W||console.log(...e)},q={...c,pick(...e){return Object.fromEntries(e.map(e=>[e,this[e]]))},omit(...e){let t=new Set(e);return Object.fromEntries(Object.entries(this).filter(([e])=>!t.has(e)))}},J=e=>{if(!e.bump)return;let r=e.bump,i=e.context.newVersion;r.forEach(r=>{let a=t(r.filePath,`utf8`),o=a.replace(r.versionPattern,`$1${i}$3`);e.dryRun||n(r.filePath,o,`utf8`),K(`Updated version in '${r.filePath}' to '${i}'`)})};var Y=`{{#>header}}
14
- ## &ensp; [\` 📦 {{tag}} \`]({{repo.homepage}}/{{#if prevTag}}compare/{{prevTag}}...{{tag}}{{else}}commits/{{tag}}{{/if}})
12
+ `)];return{type:a,scope:o||void 0,subject:c,body:f||void 0,breakingChanges:l,footer:p||void 0}},P=(e,t)=>{let n=t.breakingChangesPattern.exec(e)?.groups?.content;if(!n)throw Error(`Failed to extract breaking changes content from '${e}' using pattern "${t.breakingChangesPattern}"`);let r=[...n.matchAll(t.breakingChangeListPattern)];return r.length?r.map(e=>e[1]):n},F=(e,t)=>{let n=t.some(t=>t.email===e.email&&t.name===e.name);return{...e,hasSignedOff:n,ghLogin:e.name,ghUrl:`https://github.com/${e.name}`}},I=async(e,t)=>await Promise.all([...e.matchAll(t.refPattern)].map(e=>e.groups).filter(e=>t.refActionPattern.test(e.action)).flatMap(e=>[...e.labels.matchAll(t.refLabelPattern)].map(e=>e.groups).filter(e=>!!e.number).map(t=>({action:e.action,owner:t.owner,repo:t.repo,number:t.number})))),L=(e,t)=>{let n=e=>e.toString().padStart(2,`0`);if(t===`US`)return e.toLocaleString(`en-US`,{month:`short`,day:`numeric`,year:`numeric`});if(t===`ISO`)return e.toISOString().split(`T`)[0];let r={YYYY:e.getUTCFullYear().toString(),MM:n(e.getUTCMonth()+1),DD:n(e.getUTCDate()),HH:n(e.getUTCHours()),mm:n(e.getUTCMinutes()),ss:n(e.getUTCSeconds())};return t.replace(/YYYY|MM|DD|HH|mm|ss/g,e=>r[e])},R=/##COMMIT##\n#HASH# (?<hash>.+)?\n#MSG# (?<message>[\s\S]*?)\n#REFS#\s+(?<tagRefs>.+)?\n#AUTHOR-NAME# (?<authorName>.+)?\n#AUTHOR-EMAIL# (?<authorEmail>.+)?\n#AUTHOR-DATE# (?<authorTs>.+)?\n#COMMITTER-NAME# (?<committerName>.+)?\n#COMMITTER-EMAIL# (?<committerEmail>.+)?\n#COMMITTER-DATE# (?<committerTs>.+)?\n#GPGSIG-CODE# (?<gpgSigCode>.+)?\n#GPGSIG-KEYID# (?<gpgSigKeyId>.+)?/g,z=e=>{let t=o(`git remote get-url origin`,{encoding:`utf8`}).trim(),n=e.exec(t);if(!n?.groups)throw Error(`Couldn't parse remote URL: `+t);let{host:r,owner:i,name:a}=n.groups,s=`https://${r}/${i}/${a}`;return{host:r,owner:i,name:a,homepage:s}},B=(e,t)=>{let n=V(),r=H(t),i=``,a=``,s=``;if(e===`all`)i=`{{firstCommit}}`,a=`HEAD`;else if(e===`unreleased`)i=r[0]??`{{firstCommit}}`,a=`HEAD`;else if(e===`latest-release`)i=r[1]??`{{firstCommit}}`,a=r[0]??`HEAD`;else if(typeof e==`object`){let t=r.indexOf(e.versionTag);if(t===-1)throw Error(`Version tag '${e.versionTag}' not found`);i=r[t+1]??`{{firstCommit}}`,a=r[t]}else s=e;i&&a&&(s=i===`{{firstCommit}}`?`"${i}^!" ${a}`:`"${i}..${a}"`),s=s.replace(`{{firstCommit}}`,n);let c=o(`git log ${s} --pretty="##COMMIT##%n#HASH# %h%n#MSG# %B%n#REFS# %d%n#AUTHOR-NAME# %an%n#AUTHOR-EMAIL# %ae%n#AUTHOR-DATE# %at%n#COMMITTER-NAME# %cn%n#COMMITTER-EMAIL# %ce%n#COMMITTER-DATE# %ct%n#GPGSIG-CODE# %G?%n#GPGSIG-KEYID# %GK%n"`,{encoding:`utf8`});return[...c.matchAll(R)].map(e=>e.groups)},V=()=>o(`git rev-list --max-parents=0 HEAD`,{encoding:`utf8`}).trim(),H=e=>{let t=o(`git tag --sort=-creatordate`,{encoding:`utf8`});return t.split(`
13
+ `).filter(t=>e.test(t))};let U=!1;const W=e=>U=e,G=(...e)=>{U||console.log(...e)},K={...c,pick(...e){return Object.fromEntries(e.map(e=>[e,this[e]]))},omit(...e){let t=new Set(e);return Object.fromEntries(Object.entries(this).filter(([e])=>!t.has(e)))}},q=(e,t)=>{let n=r.compile(e);return n(t)},J=e=>{if(!e.bump)return;let r=e.bump,i=e.context.newVersion;r.forEach(r=>{let a=t(r.filePath,`utf8`),o=a.replace(r.versionPattern,`$1${i}$3`);e.dryRun||n(r.filePath,o,`utf8`),G(`Updated version in '${r.filePath}' to '${i}'`)})};var Y=`{{#>header~}}
14
+ ## &ensp; [\` 📦 {{tag}} \`]({{repo.homepage}}/{{#if prevRelease.tag}}compare/{{prevRelease.tag}}...{{tag}}{{else}}commits/{{tag}}{{/if}})
15
15
 
16
16
  {{/header}}
17
- {{#each commitTypeGroups}}
18
- ### {{{repeat '&nbsp;' 5}}}{{title}}
19
- {{#if (eq @key 'breaking')}}
20
- {{#each commits}}
21
- {{#if (isArray breakingChanges)}}
22
- {{~#each breakingChanges~}}
23
- - {{{this}}}
17
+
18
+ {{~#>main~}}
19
+ {{#each commitTypeGroups~}}
20
+ ### {{{repeat '&nbsp;' 5}}}{{title}}
21
+ {{#if (eq @key 'breaking')}}
22
+ {{#each commits}}
23
+ {{#if (isArray breakingChanges)}}
24
+ {{~#each breakingChanges~}}
25
+ - {{{this}}} {{#if (isBreakingCommitInOtherTypeGroup ../this)}}<sup>[{{../breakingChangeIndex}}]</sup>{{/if}}
26
+ {{/each}}
27
+ {{else~}}
28
+ - {{{breakingChanges}}} {{#if (isBreakingCommitInOtherTypeGroup this)}}<sup>[{{breakingChangeIndex}}]</sup>{{/if}}
29
+ {{/if}}
30
+ {{/each}}
31
+ {{else}}
32
+ {{#each commits~}}
33
+ - {{#if scope}}**{{scope}}**: {{/if}}{{{subject}}} {{" "}}
34
+ {{~#if ../../commitHyperlink~}}
35
+ [\`{{hash}}\`]({{../../repo.homepage}}/commit/{{hash}})
36
+ {{~else~}}
37
+ {{hash}}
38
+ {{~/if~}}
39
+ {{~#if breakingChanges}} ⚠️<sup>[{{breakingChangeIndex}}]</sup>{{/if}}
24
40
  {{/each}}
25
- {{else~}}
26
- - {{{breakingChanges}}}
27
41
  {{/if}}
42
+
28
43
  {{/each}}
29
- {{else}}
30
- {{#each commits}}
31
- - {{#if scope}}**{{scope}}**: {{/if}}{{{subject}}} {{#if ../../commitHyperlink}}[\`{{hash}}\`]({{../../repo.homepage}}/commit/{{hash}}){{else}}{{hash}}{{/if}}
32
- {{/each}}
33
- {{/if}}
34
-
35
- {{/each}}
36
- {{#>footer}}
37
- ##### &emsp;&ensp;&nbsp;&nbsp; [_All Release Commits_]
38
- {{~#if prevTag~}}
39
- ({{repo.homepage}}/compare/{{prevTag}}...{{tag}})
40
- {{~else~}}
41
- ({{repo.homepage}}/commits/{{tag}})
42
- {{~/if}}
43
- {{" "}} &ensp;•&ensp; _{{date}}_
44
-
45
-
46
- {{/footer}}`;const X=e=>{if(!e.changelog)return;let t=e.changelog,n=e.context.releases;if(!n)return;let i=U(e.prevReleaseTagPattern);r.registerPartial(t.compiledPartials),r.registerHelper(t.helpers);let a=t.header;n.forEach((t,r)=>{let o=n[r+1],s,c;if(o)s=o.tag,c=$(s,e.prevReleaseTagPattern);else{let n=i.indexOf(t.tag);n===-1?(s=e.context.currentTag,c=$(s,e.prevReleaseTagPattern)):(s=i[n+1],c=s&&$(s,e.prevReleaseTagPattern))}let l={...t,...e.context,prevTag:s,prevVersion:c},u=Q(Y,l);a+=u}),t.output===`stdout`?(K(`Generated changelog:`),console.log(a)):(K(`Writing changelog to file '${t.output}'`),e.dryRun||Z(t.output,a,t.prevReleaseHeaderPattern))},Z=(r,i,a)=>{let o=e(r)?t(r,{encoding:`utf8`}):``,s=o.search(a),c=o.slice(s),l=i+c;n(r,l,{encoding:`utf8`})},Q=(e,t)=>{let n=r.compile(e);return n(t)},$=(e,t)=>t.exec(e)?.groups?.version,ee=e=>{if(!e.commit)return;let t=e.commit;K(`Committing with options:`,t),t.stageAll&&!e.dryRun&&o(`git add -A`,{stdio:`inherit`}),e.dryRun||o(`git commit -m "${t.message}" ${t.signOff?`-s`:``} ${t.gpgSign?`-S`:``} ${t.extraArgs}`,{stdio:`inherit`})},te=e=>{if(!e.tag)return;let t=e.tag;K(`Tagging with options:`,t),e.dryRun||o(`git tag -a ${t.name} -m "${t.message}" ${t.gpgSign?`-s`:``} ${t.force?`-f`:``} ${t.extraArgs}`,{stdio:`inherit`})};async function ne(e){G(!!e.silent||!!e.profile&&!!e[`_${e.profile}`]?.silent);let t=await p(e);J(t),X(t),ee(t),te(t)}const re=e=>e;export{q as changelogSectionsSelector,ne as default,re as defineConfig};
44
+ {{/main}}
45
+
46
+ {{~#>footer~}}
47
+ ##### &emsp;&ensp;&nbsp;&nbsp; [_All Release Commits_]
48
+ {{~#if prevRelease.tag~}}
49
+ ({{repo.homepage}}/compare/{{prevRelease.tag}}...{{tag}})
50
+ {{~else~}}
51
+ ({{repo.homepage}}/commits/{{tag}})
52
+ {{~/if~}}
53
+ {{" "}} &ensp;•&ensp; _{{date}}_
54
+
55
+
56
+ {{/footer}}`;const X=e=>{if(!e.changelog)return;let t=e.changelog,n=e.context.releases;if(!n)return;let i=H(e.prevReleaseTagPattern);r.registerPartial(t.compiledPartials),r.registerHelper(t.helpers);let a=t.header;n.forEach((t,r)=>{let o=n[r+1];if(!o){let n=i.indexOf(t.tag);if(n!==-1){let t=i[n+1];o={tag:t,version:t&&C(t,e.prevReleaseTagPattern)}}else o={tag:e.context.currentTag,version:e.context.currentVersion}}let s={...t,...e.context,prevRelease:o},c=q(Y,s);a+=c}),t.output===`stdout`?(G(`Generated changelog:`),console.log(a)):(G(`Writing changelog to file '${t.output}'`),e.dryRun||Z(t.output,a,t.prevReleaseHeaderPattern))},Z=(r,i,a)=>{let o=e(r)?t(r,{encoding:`utf8`}):``,s=o.search(a),c=o.slice(s),l=i+c;n(r,l,{encoding:`utf8`})},Q=e=>{if(!e.commit)return;let t=e.commit;G(`Committing with options:`,t),t.stageAll&&!e.dryRun&&o(`git add -A`,{stdio:`inherit`}),e.dryRun||o(`git commit -m "${t.message}" ${t.signOff?`-s`:``} ${t.gpgSign?`-S`:``} ${t.extraArgs}`,{stdio:`inherit`})},$=e=>{if(!e.tag)return;let t=e.tag;G(`Tagging with options:`,t),e.dryRun||o(`git tag -a ${t.name} -m "${t.message}" ${t.gpgSign?`-s`:``} ${t.force?`-f`:``} ${t.extraArgs}`,{stdio:`inherit`})};async function ee(e){W(!!e.silent||!!e.profile&&!!e[`_${e.profile}`]?.silent);let t=await p(e);J(t),X(t),Q(t),$(t)}const te=e=>e;export{K as changelogSectionsSelector,ee as default,te as defineConfig};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relion",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Release workflow helper for Node.js projects.",
5
5
  "author": "Kh4f <kh4f.dev@gmail.com>",
6
6
  "license": "MIT",
@@ -55,12 +55,12 @@
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsdown",
58
- "lint": "eslint",
59
- "test": "vitest run index",
60
58
  "build:watch": "tsdown --watch",
61
59
  "build:prod": "tsdown --production",
62
- "test:watch": "vitest index",
60
+ "lint": "eslint",
63
61
  "lint:fix": "eslint --fix",
62
+ "test": "vitest run index",
63
+ "test:watch": "vitest index",
64
64
  "release": "node dist/cli.js -blct",
65
65
  "release:github": "node dist/cli.js -l -p github -L"
66
66
  }