relion 0.16.0 → 0.17.1

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/cli.js CHANGED
@@ -1 +1 @@
1
- import{resolve as e}from"node:path";import{pathToFileURL as t}from"node:url";import n from"./index.js";import{cli as r}from"cleye";import.meta.main&&await i();async function i(e,t){typeof e==`string`&&(e=e.split(` `)),e&&=[e].flat();let i=r({name:`relion`,flags:{config:{alias:`c`,type:String,description:`Path to the config file`,default:`relion.config.ts`},bump:{alias:`b`,type:Boolean,description:`Bump the version`,default:!1},changelog:{alias:`l`,type:Boolean,description:`Generate a changelog`,default:!1},commit:{alias:`m`,type:Boolean,description:`Create a commit`,default:!1},tag:{alias:`t`,type:Boolean,description:`Create a tag`,default:!1},profile:{alias:`p`,type:String,description:`Use config profile`},dry:{alias:`d`,type:Boolean,description:`Run without making any changes`,default:!1},latest:{alias:`L`,type:Boolean,description:`Use the latest-release commit range in changelog`,default:!1}}},void 0,e?[...e]:process.argv.slice(2));if(i)return t??=await a(i.flags.config),t.bump||=i.flags.bump,t.changelog||=i.flags.changelog,t.commit||=i.flags.commit,t.tag||=i.flags.tag,t.profile??=i.flags.profile,t.dryRun??=i.flags.dry,i.flags.latest&&t.changelog&&(t.changelog===!0&&(t.changelog={}),t.changelog.commitRange=`latest-release`),{inputConfig:t,...n(t)}}const a=async n=>{try{return(await import(t(e(process.cwd(),n)).href)).default}catch(e){throw Error(`Error loading config: ${e.message}`)}};export{i as runCli};
1
+ import{resolve as e}from"node:path";import{pathToFileURL as t}from"node:url";import n from"./index.js";import{cli as r}from"cleye";const i=async n=>{try{return(await import(t(e(process.cwd(),n)).href)).default}catch(e){throw Error(`Error loading config: ${e.message}`)}};import.meta.main&&await a();async function a(e,t){typeof e==`string`&&(e=e.split(` `)),e&&=[e].flat();let a=r({name:`relion`,flags:{config:{alias:`c`,type:String,description:`Path to the config file`,default:`relion.config.ts`},bump:{alias:`b`,type:Boolean,description:`Bump the version`,default:!1},changelog:{alias:`l`,type:Boolean,description:`Generate a changelog`,default:!1},commit:{alias:`m`,type:Boolean,description:`Create a commit`,default:!1},tag:{alias:`t`,type:Boolean,description:`Create a tag`,default:!1},profile:{alias:`p`,type:String,description:`Use config profile`},dry:{alias:`d`,type:Boolean,description:`Run without making any changes`,default:!1},latest:{alias:`L`,type:Boolean,description:`Use the latest-release commit range in changelog`,default:!1}}},void 0,e?[...e]:process.argv.slice(2));if(a)return t??=await i(a.flags.config),t.bump||=a.flags.bump,t.changelog||=a.flags.changelog,t.commit||=a.flags.commit,t.tag||=a.flags.tag,t.profile??=a.flags.profile,t.dryRun??=a.flags.dry,a.flags.latest&&t.changelog&&(t.changelog===!0&&(t.changelog={}),t.changelog.commitRange=`latest-release`),{inputConfig:t,...n(t)}}export{a as runCli};
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ interface UserConfig {
9
9
  tag?: boolean | TagOptions;
10
10
  newTagPrefix?: string;
11
11
  newTagFormat?: string;
12
+ versionSource?: 'versionSourceFile' | 'latest-release-tag';
12
13
  versionSourceFile?: string | VersionedFile;
13
14
  releaseType?: ReleaseType;
14
15
  zeroMajorBreakingIsMinor?: boolean;
@@ -17,7 +18,7 @@ interface UserConfig {
17
18
  prevReleaseTagPattern?: RegExp;
18
19
  dryRun?: boolean;
19
20
  profile?: string;
20
- silent?: boolean;
21
+ logLevel?: 'info' | 'info-clean' | 'silent';
21
22
  [profile: `_${string}`]: UserConfig | undefined;
22
23
  }
23
24
  type OptionalKeys = 'releaseType' | 'context' | 'profile' | 'newTagPrefix';
@@ -35,7 +36,7 @@ interface TransformedConfig extends Omit<MergedConfig, 'changelog'> {
35
36
  interface ResolvedConfig extends TransformedConfig {
36
37
  context: ResolvedContext;
37
38
  }
38
- type BumpFiles = (string | VersionedFile)[];
39
+ type BumpFiles = ('!versionSourceFile' | VersionedFile | (string & {}))[];
39
40
  interface ChangelogOptions {
40
41
  output?: 'stdout' | (string & {});
41
42
  commitRange?: CommitRange;
@@ -44,7 +45,7 @@ interface ChangelogOptions {
44
45
  prevReleaseHeaderPattern?: RegExp;
45
46
  groupCommitsByScope?: boolean;
46
47
  helpers?: HelperDeclareSpec;
47
- partials?: Record<string, string>;
48
+ partials?: Record<string, string | ((fallback: string) => string)>;
48
49
  }
49
50
  type CompleteChangelogOptions = Required<ChangelogOptions>;
50
51
  interface ResolvedChangelogOptions extends Omit<CompleteChangelogOptions, 'partials'> {
@@ -102,16 +103,18 @@ interface ResolvedContext extends Required<Context> {
102
103
  commits: ResolvedCommit[];
103
104
  releases: ReleaseWithTypeGroups[] | null;
104
105
  }
106
+ type LogLevel = 'info' | 'info-clean' | 'silent';
105
107
  type CommitRange = 'all' | 'unreleased' | 'latest-release' | {
106
108
  releaseTag: string;
107
109
  } | (string & {});
108
110
  type ReleaseType = 'major' | 'minor' | 'patch';
109
111
  interface VersionedFile {
110
- filePath: string;
111
- versionPattern: RegExp;
112
+ file: string;
113
+ pattern: RegExp;
114
+ replacement: string;
112
115
  }
113
- interface DefaultVersionedFile extends Omit<VersionedFile, 'filePath'> {
114
- filePathRegex: RegExp;
116
+ interface DefaultVersionedFile extends Omit<VersionedFile, 'file'> {
117
+ file: RegExp;
115
118
  }
116
119
  //#endregion
117
120
  //#region src/enums.d.ts
@@ -302,11 +305,6 @@ interface RelionResult {
302
305
  generatedChangelog: string | null;
303
306
  commitCommand: string | null;
304
307
  tagCommand: string | null;
305
- bumpResults: BumpResult[] | null;
306
- }
307
- interface BumpResult extends VersionedFile {
308
- oldVersion: string | null;
309
- newVersion: string;
310
308
  }
311
309
  //#endregion
312
310
  //#region src/relion.d.ts
@@ -318,4 +316,4 @@ declare const changelogSectionsSelector: ChangelogSectionsSelector;
318
316
  //#region src/index.d.ts
319
317
  declare const defineConfig: (config: UserConfig) => UserConfig;
320
318
  //#endregion
321
- export { BumpFiles, BumpResult, 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, RelionResult, RepoInfo, ResolvedChangelogOptions, ResolvedCommit, ResolvedConfig, ResolvedContext, ScopeGroup, TagOptions, TransformedConfig, TypeGroupDefinition, TypeGroupsMap, UserConfig, VersionedFile, changelogSectionsSelector, relion as default, defineConfig };
319
+ export { BumpFiles, ChangelogOptions, ChangelogSectionsSelector, CommitMessage, CommitOptions, CommitRange, CommitsParser, CompleteChangelogOptions, CompleteCommitOptions, CompleteCommitsParser, CompleteTagOptions, Context, Contributor, DefaultChangelogSections, DefaultVersionedFile, FalseOrComplete, FilledTypeGroup, FilledTypeGroupMap, GithubUserInfo, GpgSig, GpgSigCode, LogLevel, MergedConfig, ParsedCommit, RawCommit, RawReference, RefLabel, Reference, ReleaseContext, ReleaseType, ReleaseWithFlatCommits, ReleaseWithTypeGroups, RelionResult, RepoInfo, ResolvedChangelogOptions, ResolvedCommit, ResolvedConfig, ResolvedContext, ScopeGroup, TagOptions, TransformedConfig, TypeGroupDefinition, TypeGroupsMap, UserConfig, VersionedFile, changelogSectionsSelector, relion as default, defineConfig };
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import r from"semver";import{createHash as i}from"node:crypto";import{execSync as a}from"node:child_process";import o from"handlebars";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
1
+ import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import r from"semver";import{createHash as i}from"node:crypto";import{execSync as a}from"node:child_process";import o from"handlebars";const s={bump:!1,changelog:!1,commit:!1,tag:!1,versionSource:`versionSourceFile`,versionSourceFile:`./package.json`,newTagFormat:`v{{version}}`,prevReleaseTagPattern:/^v?(?<version>\d+\.\d+\.\d+)/,zeroMajorBreakingIsMinor:!0,dryRun:!1,logLevel:`info`,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
4
  `,prevReleaseHeaderPattern:/^##.*?\d+\.\d+\.\d+/m,groupCommitsByScope:!0,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)),isSingle:e=>e.length===1,or:(...e)=>e.slice(0,-1).some(Boolean),not:e=>!e},partials:{br:`
5
- `}},u={message:`release({{repo.name}}): {{newTag}}`,signOff:!1,gpgSign:!1,stageAll:!0,extraArgs:null},d={name:`{{newTag}}`,message:`release({{repo.name}}): {{newTag}}`,gpgSign:!1,force:!1,extraArgs:null},f=[{filePathRegex:/package\.json$/,versionPattern:/(^.*?"version".*?")(.*?)(")/s},{filePathRegex:/package-lock\.json$/,versionPattern:/(^.*?"version".*?"|"packages".*?"".*"version".*?")(.*?)(")/gs}],p=e=>{let t=m(e),n=h(t),r=g(n),i=_(r);return S(i)},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);return n&&r?{...e,...t}:t??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[r];if(Array.isArray(e))return[r,...e.map(e=>typeof e==`string`?t(e):e)];throw Error(`Invalid value for bump. It should be a boolean or an array.`)},r=typeof e.versionSourceFile==`string`?t(e.versionSourceFile):e.versionSourceFile;return{...e,versionSourceFile:r,bump:n(e.bump),changelog:e.changelog===!1?!1:{...e.changelog,compiledPartials:X(e.changelog.partials)}}},_=e=>{let t=e.context??{},n=V(e.commitsParser.remoteUrlPattern),r=t.currentVersion??C(e.versionSourceFile),i=t.currentTag??W(e.prevReleaseTagPattern)[0],a=t.newVersion??T(e,r),o=t.newTag??(e.newTagPrefix?e.newTagPrefix+a:e.newTagFormat.replace(`{{version}}`,a)),s=e.changelog?e.changelog.commitRange:`unreleased`,c=t.commits?t.commits.map(t=>typeof t==`object`&&`message`in t||typeof t==`string`?M(t,e.commitsParser,e.prevReleaseTagPattern):t).filter(e=>e!=null):j(s,e.commitsParser,e.prevReleaseTagPattern),l=v(c,o,e.commitsParser.revertCommitBodyPattern),u=e.changelog?y(l,e.changelog.sections,e.prevReleaseTagPattern,e.changelog.groupCommitsByScope):null,{commits:d,...f}=t;return{...e,context:{currentVersion:r,currentTag:i,newVersion:a,newTag:o,commits:l,releases:u,...f,repo:{...n,...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,r)=>{let i={};return e.forEach(e=>{let t=e.associatedReleaseTag;t in i?i[t].commits.push(e):i[t]={tag:t,version:w(t,n),date:e.date,commits:[e]}}),Object.values(i).map(({commits:e,...n})=>({...n,commitTypeGroups:b(e,t,r)})).filter(e=>Object.keys(e.commitTypeGroups).length)},b=(e,t,n)=>{let r=Object.fromEntries(Object.entries(t).map(([e,{filter:t,...n}])=>[e,{...n,commits:[]}]));e.forEach(e=>{let n=!!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(n&&!a&&s.includes(`breaking`)){r[o].commits.push(e),a=!0;continue}if(!i&&(s.includes(e.type)||s.includes(`*`))&&(r[o].commits.push(e),i=!0),i&&(!n||a))return}});let i=Object.entries(r).filter(([e,t])=>t.commits.length);return Object.fromEntries(n?i.map(([e,t])=>[e,{...t,scopeGroups:x(t.commits)}]):i)},x=e=>{let t={};return e.forEach(e=>{let n=e.scope??``;t[n]??=[],t[n].push(e)}),Object.entries(t).map(([e,t])=>({scope:e,commits:t}))},S=e=>({...e,commit:e.commit?{...e.commit,message:Y(e.commit.message,e.context)}:e.commit,tag:e.tag?{...e.tag,name:Y(e.tag.name,e.context),message:Y(e.tag.message,e.context)}:e.tag}),C=e=>{let n=t(e.filePath,`utf8`),i=e.versionPattern.exec(n)?.[2];if(!i)throw Error(`Version not found in '${e.filePath}' with pattern '${e.versionPattern}'`);if(!r.valid(i))throw Error(`Invalid version format in '${e.filePath}': '${i}'`);return q(`Current version from '${e.filePath}': '${i}'`),i},w=(e,t)=>t.exec(e)?.groups?.version,T=(e,t)=>{if(e.context?.newVersion){if(!r.valid(e.context.newVersion))throw Error(`Invalid release version format: '${e.context.newVersion}'`);return e.context.newVersion}let n;if(e.releaseType)n=e.releaseType;else{let i=j(`unreleased`,e.commitsParser,e.prevReleaseTagPattern);n=E(i),e.zeroMajorBreakingIsMinor&&r.major(t)===0&&n===`major`&&(n=`minor`)}let i=D(t,n);return q(`Determined new version: '${i}' (release type: '${n}')`),i},E=e=>e.some(e=>e.breakingChanges)?`major`:e.some(e=>e.type===`feat`)?`minor`:`patch`,D=(e,t)=>r.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=new Map;let A;const j=(e,t,n)=>{let r=Array.isArray(e)?e:H(e,n),i=t,a=r.map(e=>M(e,i,n)).filter(e=>e!==null);return A=void 0,a},M=(e,t,n)=>{typeof e==`string`&&(e={message:e});let{tagRefs:r,hash:i=N(e.message)}=e;if(k.has(i))return k.get(i)??null;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),(f?[...f.matchAll(t.coAuthorPattern)].map(e=>e.groups).map(e=>I(e,m)):[]).forEach(e=>g(e));let y=L(f??``,t),b=e.gpgSigCode?{code:e.gpgSigCode,label:O[e.gpgSigCode],keyId:e.gpgSigKeyId}:void 0,x=e[t.dateSource===`committerDate`?`committerTs`:`authorTs`];typeof x==`string`&&(x=R(new Date(x*1e3),t.dateFormat));let S=p.find(e=>n.exec(e)),C=S??A;C&&(A=C);let w={hash:i,type:s,scope:c,subject:l,body:u,breakingChanges:d,footer:f,committer:v,gpgSig:b,date:x,releaseTag:S,associatedReleaseTag:C,tags:p.length?p:void 0,authors:h.length?h:void 0,refs:y.length?y:void 0};return i&&!k.has(i)&&k.set(i,w),w},N=e=>`fake_`+i(`sha256`).update(e,`utf8`).digest(`hex`).slice(0,7),P=(e,t)=>{let[n,...r]=e.split(`
5
+ `}},u={message:`release({{repo.name}}): {{newTag}}`,signOff:!1,gpgSign:!1,stageAll:!0,extraArgs:null},d={name:`{{newTag}}`,message:`release({{repo.name}}): {{newTag}}`,gpgSign:!1,force:!1,extraArgs:null},f=[{file:/package\.json$/,pattern:/(^.*?"version".*?")(.*?)(")/s,replacement:`$1{{newVersion}}$3`},{file:/package-lock\.json$/,pattern:/(^.*?"version".*?"|"packages".*?"".*"version".*?")(.*?)(")/gs,replacement:`$1{{newVersion}}$3`}],p=e=>{let t=m(e),n=h(t),r=g(n),i=_(r);return S(i)},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);return n&&r?{...e,...t}:t??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.file.test(e));if(t)return{...t,file:e};throw Error(`File ${e} doesn't match any default versioned files. Please provide a custom pattern for this file.`)},n=e=>{if(e===!1)return!1;if(e===!0)return[r];if(Array.isArray(e)){let n=e.filter(e=>e!==`!versionSourceFile`);return[...e.includes(`!versionSourceFile`)?[]:[r],...n.map(e=>typeof e==`string`?t(e):e)]}throw Error(`Invalid value for bump. It should be a boolean or an array.`)},r=typeof e.versionSourceFile==`string`?t(e.versionSourceFile):e.versionSourceFile;return{...e,versionSourceFile:r,bump:n(e.bump),changelog:e.changelog===!1?!1:{...e.changelog,compiledPartials:Q(e.changelog.partials)}}},_=e=>{let t=e.context??{},n=V(e.commitsParser.remoteUrlPattern),r=t.currentVersion??(e.versionSource===`latest-release-tag`?w(W(e.prevReleaseTagPattern)[0],e.prevReleaseTagPattern)??`0.0.0`:C(e.versionSourceFile));q(`Current version (from ${e.versionSource===`latest-release-tag`?`latest release tag`:`'${e.versionSourceFile.file}'`}): '${r}'`);let i=t.currentTag??W(e.prevReleaseTagPattern)[0],a=t.newVersion??T(e,r),o=t.newTag??(e.newTagPrefix===void 0?e.newTagFormat.replace(`{{version}}`,a):e.newTagPrefix+a),s=e.changelog?e.changelog.commitRange:`unreleased`,c=t.commits?t.commits.map(t=>typeof t==`object`&&`message`in t||typeof t==`string`?M(t,e.commitsParser,e.prevReleaseTagPattern):t).filter(e=>e!=null):j(s,e.commitsParser,e.prevReleaseTagPattern),l=v(c,o,e.commitsParser.revertCommitBodyPattern),u=e.changelog?y(l,e.changelog.sections,e.prevReleaseTagPattern,e.changelog.groupCommitsByScope):null,{commits:d,...f}=t;return{...e,context:{currentVersion:r,currentTag:i,newVersion:a,newTag:o,commits:l,releases:u,...f,repo:{...n,...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,r)=>{let i={};return e.forEach(e=>{let t=e.associatedReleaseTag;t in i?i[t].commits.push(e):i[t]={tag:t,version:w(t,n),date:e.date,commits:[e]}}),Object.values(i).map(({commits:e,...n})=>({...n,commitTypeGroups:b(e,t,r)})).filter(e=>Object.keys(e.commitTypeGroups).length)},b=(e,t,n)=>{let r=Object.fromEntries(Object.entries(t).map(([e,{filter:t,...n}])=>[e,{...n,commits:[]}]));e.forEach(e=>{let n=!!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(n&&!a&&s.includes(`breaking`)){r[o].commits.push(e),a=!0;continue}if(!i&&(s.includes(e.type)||s.includes(`*`))&&(r[o].commits.push(e),i=!0),i&&(!n||a))return}});let i=Object.entries(r).filter(([e,t])=>t.commits.length);return Object.fromEntries(n?i.map(([e,t])=>[e,{...t,scopeGroups:x(t.commits)}]):i)},x=e=>{let t={};return e.forEach(e=>{let n=e.scope??``;t[n]??=[],t[n].push(e)}),Object.entries(t).map(([e,t])=>({scope:e,commits:t}))},S=e=>({...e,commit:e.commit?{...e.commit,message:Z(e.commit.message,e.context)}:e.commit,tag:e.tag?{...e.tag,name:Z(e.tag.name,e.context),message:Z(e.tag.message,e.context)}:e.tag}),C=e=>{let n=t(e.file,`utf8`),i=e.pattern.exec(n)?.[2];if(!i)throw Error(`Version not found in '${e.file}' with pattern '${e.pattern}'`);if(!r.valid(i))throw Error(`Invalid version format in '${e.file}': '${i}'`);return i},w=(e,t)=>t.exec(e)?.groups?.version,T=(e,t)=>{if(e.context?.newVersion){if(!r.valid(e.context.newVersion))throw Error(`Invalid release version format: '${e.context.newVersion}'`);return e.context.newVersion}let n;if(e.releaseType)n=e.releaseType;else{let i=j(`unreleased`,e.commitsParser,e.prevReleaseTagPattern);n=E(i),e.zeroMajorBreakingIsMinor&&r.major(t)===0&&n===`major`&&(n=`minor`)}let i=D(t,n);return q(`Determined new version: '${i}' (release type: '${n}')`),i},E=e=>e.some(e=>e.breakingChanges)?`major`:e.some(e=>e.type===`feat`)?`minor`:`patch`,D=(e,t)=>r.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=new Map;let A;const j=(e,t,n)=>{let r=Array.isArray(e)?e:H(e,n),i=t,a=r.map(e=>M(e,i,n)).filter(e=>e!==null);return A=void 0,a},M=(e,t,n)=>{typeof e==`string`&&(e={message:e});let{tagRefs:r,hash:i=N(e.message)}=e;if(k.has(i))return k.get(i)??null;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 J(`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),(f?[...f.matchAll(t.coAuthorPattern)].map(e=>e.groups).map(e=>I(e,m)):[]).forEach(e=>g(e));let y=L(f??``,t),b=e.gpgSigCode?{code:e.gpgSigCode,label:O[e.gpgSigCode],keyId:e.gpgSigKeyId}:void 0,x=e[t.dateSource===`committerDate`?`committerTs`:`authorTs`];typeof x==`string`&&(x=R(new Date(x*1e3),t.dateFormat));let S=p.find(e=>n.exec(e)),C=S??A;C&&(A=C);let w={hash:i,type:s,scope:c,subject:l,body:u,breakingChanges:d,footer:f,committer:v,gpgSig:b,date:x,releaseTag:S,associatedReleaseTag:C,tags:p.length?p:void 0,authors:h.length?h:void 0,refs:y.length?y:void 0};return i&&!k.has(i)&&k.set(i,w),w},N=e=>`fake_`+i(`sha256`).update(e,`utf8`).digest(`hex`).slice(0,7),P=(e,t)=>{let[n,...r]=e.split(`
6
6
 
7
7
  `),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(`
8
8
 
@@ -11,7 +11,7 @@ import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import
11
11
  `),r.slice(d).join(`
12
12
 
13
13
  `)];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=(e,t)=>[...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=new Map,V=e=>{let t=a(`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:o}=n.groups,s=`https://${r}/${i}/${o}`;return{host:r,owner:i,name:o,homepage:s}},H=(e,t)=>{let n=U(),r=W(t),i=``,o=``,s=``;if(e===`all`)i=`{{firstCommit}}`,o=`HEAD`;else if(e===`unreleased`)i=r[0]??`{{firstCommit}}`,o=`HEAD`;else if(e===`latest-release`)i=r[1]??`{{firstCommit}}`,o=r[0]??`HEAD`;else if(typeof e==`object`){let t=r.indexOf(e.releaseTag);if(t===-1)throw Error(`Release tag '${e.releaseTag}' not found`);i=r[t+1]??`{{firstCommit}}`,o=r[t]}else s=e;return i&&o&&(s=i===`{{firstCommit}}`?`"${i}^!" ${o}`:`"${i}..${o}"`),s=s.replace(`{{firstCommit}}`,n),[...a(`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`}).matchAll(z)].map(e=>e.groups)},U=()=>a(`git rev-list --max-parents=0 HEAD`,{encoding:`utf8`}).trim(),W=e=>{let t=e.toString();if(B.has(t))return B.get(t)??[];let n=a(`git tag --sort=-creatordate`,{encoding:`utf8`}).split(`
14
- `).filter(t=>e.test(t));return B.set(t,n),n};let G=!1;const K=e=>G=e,q=(...e)=>{G||console.log(...e)},J={...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)))}},Y=(e,t)=>o.compile(e)(t),X=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,o.compile(t)])),Z=e=>{if(!e.bump)return null;let r=e.bump,i=e.context.newVersion;return r.map(r=>{let a=t(r.filePath,`utf8`),o=r.versionPattern.exec(a)?.[2]??null,s=a.replace(r.versionPattern,`$1${i}$3`);return e.dryRun||n(r.filePath,s,`utf8`),q(`Updated version in '${r.filePath}' to '${i}'`),{...r,oldVersion:o,newVersion:i}})};var Q=`{{#*inline "compareLink"~}}
14
+ `).filter(t=>e.test(t));return B.set(t,n),n};let G=`info`;const K=e=>G=e,q=(...e)=>{G!==`silent`&&console.log(...e)},J=(...e)=>{G===`info`&&console.warn(...e)},Y={...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)))}};var X=`{{#*inline "compareLink"~}}
15
15
  {{repo.homepage~}}
16
16
  {{#if prevRelease.tag~}}
17
17
  /compare/{{prevRelease.tag}}...
@@ -80,4 +80,4 @@ import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import
80
80
  ##### &emsp;&ensp;&nbsp;&nbsp; [_All Release Commits_]({{>compareLink}}) &ensp;•&ensp; _{{date}}_
81
81
 
82
82
 
83
- {{/footer}}`;const $=e=>{if(!e.changelog)return null;let t=e.changelog,n=e.context.releases;if(!n)return null;let r=W(e.prevReleaseTagPattern);o.registerPartial(t.compiledPartials),o.registerHelper(t.helpers);let i=t.header;return n.forEach((t,a)=>{let o=n[a+1];if(!o){let n=r.indexOf(t.tag);if(n!==-1){let t=r[n+1];o={tag:t,version:t&&w(t,e.prevReleaseTagPattern)}}else o={tag:e.context.currentTag,version:e.context.currentVersion}}let s={...t,...e.context,prevRelease:o},c=Y(Q,s);i+=c}),t.output===`stdout`?(q(`Generated changelog:`),console.log(i)):(q(`Writing changelog to file '${t.output}'`),e.dryRun||ee(t.output,i,t.prevReleaseHeaderPattern)),i},ee=(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`})},te=e=>{if(!e.commit)return null;let t=e.commit,n=[t.stageAll&&`git add -A &&`,`git commit -m "${t.message}"`,t.signOff&&`-s`,t.gpgSign&&`-S`,t.extraArgs].filter(Boolean).join(` `);return q(`Committing with command: '${n}'`),e.dryRun||a(n,{stdio:`inherit`}),n},ne=e=>{if(!e.tag)return null;let t=e.tag,n=[`git tag -a ${t.name}`,`-m "${t.message}"`,t.gpgSign&&`-s`,t.force&&`-f`,t.extraArgs].filter(Boolean).join(` `);return q(`Tagging with command: '${n}'`),e.dryRun||a(n,{stdio:`inherit`}),n};function re(e){K(!!e.silent||!!e.profile&&!!e[`_${e.profile}`]?.silent);let t=p(e),n=Z(t),r=$(t),i=te(t),a=ne(t);return{resolvedConfig:t,generatedChangelog:r,commitCommand:i,tagCommand:a,bumpResults:n}}const ie=e=>e;export{J as changelogSectionsSelector,re as default,ie as defineConfig};
83
+ {{/footer}}`;const Z=(e,t)=>o.compile(e)(t),Q=e=>{let t=Object.fromEntries(Object.entries(e).map(([e,t])=>[e,typeof t==`function`?$(e,t):t]));return Object.fromEntries(Object.entries(t).map(([e,t])=>[e,o.compile(t)]))},$=(e,t)=>{let n=RegExp(`${e}.*?}}\\s*(.*?){{~?/${e}`,`s`).exec(X)?.[1];if(!n)throw Error(`Partial "${e}" not found in the release template.`);return t(n)},ee=e=>{if(!e.bump)return null;let r=e.bump,i=e.context.newVersion;r.forEach(r=>{let a=t(r.file,`utf8`).replace(r.pattern,r.replacement.replace(`{{newVersion}}`,i));e.dryRun||n(r.file,a,`utf8`),q(`Updated version in '${r.file}' to '${i}'`)})},te=e=>{if(!e.changelog)return null;let t=e.changelog,n=e.context.releases;if(!n)return null;let r=W(e.prevReleaseTagPattern);o.registerPartial(t.compiledPartials),o.registerHelper(t.helpers);let i=t.header;return n.forEach((t,a)=>{let o=n[a+1];if(!o){let n=r.indexOf(t.tag);if(n!==-1){let t=r[n+1];o={tag:t,version:t&&w(t,e.prevReleaseTagPattern)}}else o={tag:e.context.currentTag,version:e.context.currentVersion}}let s={...t,...e.context,prevRelease:o},c=Z(X,s);i+=c}),t.output===`stdout`||e.dryRun?(q(`Generated changelog:`),console.log(i)):(q(`Writing changelog to file '${t.output}'`),ne(t.output,i,t.prevReleaseHeaderPattern)),i},ne=(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`})},re=e=>{if(!e.commit)return null;let t=e.commit,n=[t.stageAll&&`git add -A &&`,`git commit -m "${t.message}"`,t.signOff&&`-s`,t.gpgSign&&`-S`,t.extraArgs].filter(Boolean).join(` `);return q(`Committing with command: '${n}'`),e.dryRun||a(n,{stdio:`inherit`}),n},ie=e=>{if(!e.tag)return null;let t=e.tag,n=[`git tag -a ${t.name}`,`-m "${t.message}"`,t.gpgSign&&`-s`,t.force&&`-f`,t.extraArgs].filter(Boolean).join(` `);return q(`Tagging with command: '${n}'`),e.dryRun||a(n,{stdio:`inherit`}),n};function ae(e){K((e.logLevel??(!!e.profile&&e[`_${e.profile}`]?.logLevel))||`info`);let t=p(e);ee(t);let n=te(t),r=re(t),i=ie(t);return{resolvedConfig:t,generatedChangelog:n,commitCommand:r,tagCommand:i}}const oe=e=>e;export{Y as changelogSectionsSelector,ae as default,oe as defineConfig};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relion",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "description": "Release workflow helper for Node.js projects.",
5
5
  "author": "Kh4f <kh4f.dev@gmail.com>",
6
6
  "license": "MIT",
@@ -42,12 +42,12 @@
42
42
  "@types/semver": "^7.7.1",
43
43
  "eslint": "^9.36.0",
44
44
  "globals": "^16.4.0",
45
- "lint-staged": "^16.1.6",
45
+ "lint-staged": "^16.2.1",
46
46
  "simple-git-hooks": "^2.13.1",
47
- "tsdown": "^0.15.4",
48
- "tsx": "^4.20.5",
47
+ "tsdown": "^0.15.5",
48
+ "tsx": "^4.20.6",
49
49
  "typescript": "^5.9.2",
50
- "typescript-eslint": "^8.42.0",
50
+ "typescript-eslint": "^8.44.1",
51
51
  "vitest": "^3.2.4"
52
52
  },
53
53
  "simple-git-hooks": {