relion 0.9.0 → 0.11.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
@@ -40,7 +40,7 @@ type BumpFiles = (string | VersionedFile)[];
40
40
  interface ChangelogOptions {
41
41
  output?: 'stdout' | (string & {});
42
42
  commitRange?: CommitRange;
43
- sections?: ChangelogSectionsMap;
43
+ sections?: TypeGroupsMap;
44
44
  header?: string;
45
45
  prevReleaseHeaderPattern?: RegExp;
46
46
  helpers?: HelperDeclareSpec;
@@ -79,7 +79,7 @@ interface CommitsParser {
79
79
  remoteUrlPattern?: RegExp;
80
80
  signerPattern?: RegExp;
81
81
  dateSource?: 'authorDate' | 'committerDate';
82
- dateFormat?: string;
82
+ dateFormat?: 'US' | 'ISO' | (string & {});
83
83
  revertCommitBodyPattern?: RegExp;
84
84
  }
85
85
  type CompleteCommitsParser = Required<CommitsParser>;
@@ -100,18 +100,11 @@ interface Context {
100
100
  }
101
101
  interface ResolvedContext extends Required<Context> {
102
102
  commits: ResolvedCommit[];
103
- releases: ReleaseWithGroupedCommits[] | null;
103
+ releases: ReleaseWithTypeGroups[] | null;
104
104
  }
105
105
  type CommitRange = 'all' | 'unreleased' | 'latest-release' | {
106
106
  versionTag: string;
107
- } | {
108
- from: 'firstCommit' | (string & {});
109
- } | {
110
- to: 'HEAD' | (string & {});
111
- } | {
112
- from: 'firstCommit' | (string & {});
113
- to: 'HEAD' | (string & {});
114
- };
107
+ } | (string & {});
115
108
  type ReleaseType = 'major' | 'minor' | 'patch';
116
109
  interface VersionedFile {
117
110
  filePath: string;
@@ -159,14 +152,14 @@ interface ParsedCommit extends CommitMessage {
159
152
  refs?: Reference[];
160
153
  gpgSig?: GpgSig;
161
154
  date?: string;
162
- associatedReleaseTag?: string | null;
155
+ releaseTag?: string;
156
+ associatedReleaseTag?: string;
163
157
  isReverted?: 'inTheSameRelease' | 'inOtherRelease' | null;
164
158
  }
165
- interface ParsedCommitWithReleaseTag extends ParsedCommit {
166
- associatedReleaseTag: string;
167
- }
168
- interface ResolvedCommit extends ParsedCommitWithReleaseTag {
169
- isReverted: 'inTheSameRelease' | 'inOtherRelease' | null;
159
+ interface ResolvedCommit extends ParsedCommit {
160
+ associatedReleaseTag: NonNullable<ParsedCommit['associatedReleaseTag']>;
161
+ isReverted: NonNullable<ParsedCommit['isReverted']> | null;
162
+ breakingChangeIndex?: number;
170
163
  }
171
164
  interface CommitMessage {
172
165
  type: string;
@@ -271,26 +264,26 @@ declare const defaultChangelogSections: {
271
264
  };
272
265
  //#endregion
273
266
  //#region src/types/changelog.d.ts
274
- interface ChangelogSectionDefinition {
267
+ interface TypeGroupDefinition {
275
268
  title: string;
276
269
  commitType: 'breaking' | '*' | (string & {}) | string[];
277
270
  filter?: (commit: ResolvedCommit) => boolean;
278
271
  }
279
- type ChangelogSectionsMap = Record<string, ChangelogSectionDefinition>;
280
- interface ResolvedChangelogSection extends Omit<ChangelogSectionDefinition, 'filter'> {
272
+ type TypeGroupsMap = Record<string, TypeGroupDefinition>;
273
+ interface FilledTypeGroup extends Omit<TypeGroupDefinition, 'filter'> {
281
274
  commits: ResolvedCommit[];
282
275
  }
283
- type ResolvedChangelogSectionsMap = Record<string, ResolvedChangelogSection>;
276
+ type FilledTypeGroupMap = Record<string, FilledTypeGroup>;
284
277
  interface ReleaseWithFlatCommits {
285
278
  tag: string;
286
279
  version?: string;
287
280
  date?: string;
288
281
  commits: ResolvedCommit[];
289
282
  }
290
- interface ReleaseWithGroupedCommits extends Omit<ReleaseWithFlatCommits, 'commits'> {
291
- commitTypeGroups: ResolvedChangelogSectionsMap;
283
+ interface ReleaseWithTypeGroups extends Omit<ReleaseWithFlatCommits, 'commits'> {
284
+ commitTypeGroups: FilledTypeGroupMap;
292
285
  }
293
- interface ReleaseContext extends ReleaseWithGroupedCommits, ResolvedContext {
286
+ interface ReleaseContext extends ReleaseWithTypeGroups, ResolvedContext {
294
287
  prevTag?: string;
295
288
  prevVersion?: string;
296
289
  }
@@ -307,4 +300,4 @@ declare const defineConfig: (config: UserConfig) => UserConfig;
307
300
  //#region src/utils/sections-selector.d.ts
308
301
  declare const changelogSectionsSelector: ChangelogSectionsSelector;
309
302
  //#endregion
310
- export { BumpFiles, ChangelogOptions, ChangelogSectionDefinition, ChangelogSectionsMap, ChangelogSectionsSelector, CommitMessage, CommitOptions, CommitRange, CommitsParser, CompleteChangelogOptions, CompleteCommitOptions, CompleteCommitsParser, CompleteTagOptions, Context, ContextualConfig, Contributor, DefaultChangelogSections, DefaultVersionedFile, FalseOrComplete, GithubUserInfo, GpgSig, GpgSigCode, MergedConfig, ParsedCommit, RawCommit, RawReference, RefLabel, Reference, ReleaseContext, ReleaseType, ReleaseWithFlatCommits, ReleaseWithGroupedCommits, RepoInfo, ResolvedChangelogOptions, ResolvedChangelogSection, ResolvedChangelogSectionsMap, ResolvedCommit, ResolvedConfig, ResolvedContext, TagOptions, TransformedConfig, UserConfig, VersionedFile, changelogSectionsSelector, relion as default, defineConfig };
303
+ 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 };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
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:`YYYY-MM-DD`,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{{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
2
2
 
3
3
 
4
- `,prevReleaseHeaderPattern:/^##.*?\d+\.\d+\.\d+/m,helpers:{eq:(e,t)=>e===t,repeat:(e,t)=>e.repeat(t)},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=null;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=null,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))??A;C&&(A=C);let w={hash:i,type:s,scope:c,subject:l,body:u,breakingChanges:d,footer:f,committer:v,gpgSig:x,date:S,associatedReleaseTag:C,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]=w),w},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=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=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: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,{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))},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(`
5
5
 
6
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(`
7
7
 
@@ -9,26 +9,48 @@ import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import
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`),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;if(e===`all`)i=n,a=`HEAD`;else if(e===`unreleased`)i=r[0]??n,a=`HEAD`;else if(e===`latest-release`)i=r[1]??n,a=r[0]??`HEAD`;else if(`from`in e||`to`in e){let t=`from`in e?e.from:`firstCommit`;i=t===`firstCommit`?n:t,a=`to`in e?e.to:`HEAD`}else if(`versionTag`in e){let t=r.indexOf(e.versionTag);if(t===-1)throw Error(`Version tag '${e.versionTag}' not found`);a=r[t],i=r[t+1]??n}else throw Error(`Invalid commit range provided`);let s=o(`git log "${i}^..${a}" --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[...s.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}},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}})
15
15
 
16
16
  {{/header}}
17
- {{#each commitTypeGroups}}
18
- ### {{{repeat '&nbsp;' 5}}}{{title}}
19
- {{#if (eq @key 'breaking')}}
20
- {{#each commits}}
21
- * {{{breakingChanges}}}
22
- {{/each}}
23
- {{else}}
24
- {{#each commits}}
25
- * {{#if scope}}**{{scope}}**: {{/if}}{{{subject}}} {{#if ../../commitHyperlink}}[\`{{hash}}\`]({{../../repo.homepage}}/commit/{{hash}}){{else}}{{hash}}{{/if}}
26
- {{/each}}
27
- {{/if}}
28
-
29
- {{/each}}
30
- {{#>footer}}
31
- ##### &emsp;&ensp;&nbsp;&nbsp; [_All Release Commits_]{{#if prevTag}}({{repo.homepage}}/compare/{{prevTag}}...{{tag}}){{else}}\`{{tag}}\`]({{repo.homepage}}/commits/{{tag}}){{/if}} &ensp;•&ensp; _{{date}}_
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}}
40
+ {{/each}}
41
+ {{/if}}
42
+
43
+ {{/each}}
44
+ {{/main}}
45
+
46
+ {{~#>footer~}}
47
+ ##### &emsp;&ensp;&nbsp;&nbsp; [_All Release Commits_]
48
+ {{~#if prevTag~}}
49
+ ({{repo.homepage}}/compare/{{prevTag}}...{{tag}})
50
+ {{~else~}}
51
+ ({{repo.homepage}}/commits/{{tag}})
52
+ {{~/if~}}
53
+ {{" "}} &ensp;•&ensp; _{{date}}_
32
54
 
33
55
 
34
56
  {{/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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relion",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Release workflow helper for Node.js projects.",
5
5
  "author": "Kh4f <kh4f.dev@gmail.com>",
6
6
  "license": "MIT",