22import { SubprocessError , type Result , colorize , exec , logVerbose , reNewline , setVerbose , tomlGetString } from "./utils.ts" ;
33import { parseArgs } from "node:util" ;
44import { basename , dirname , join , relative , resolve } from "node:path" ;
5- import { cwd , exit , stdout } from "node:process" ;
6- import { EOL , platform } from "node:os" ;
5+ import { cwd , exit , platform , stdout } from "node:process" ;
76import { readFileSync , writeFileSync , accessSync , truncateSync } from "node:fs" ;
87import pkg from "./package.json" with { type : "json" } ;
98
109export type SemverLevel = "patch" | "minor" | "major" | "prerelease" ;
1110
11+ const EOL = platform === "win32" ? "\r\n" : "\n" ;
1212const reEscapeChars = / [ | \\ { } ( ) [ \] ^ $ + * ? . - ] / g;
1313const reSemver = / ^ ( 0 | [ 1 - 9 ] \d * ) \. ( 0 | [ 1 - 9 ] \d * ) \. ( 0 | [ 1 - 9 ] \d * ) (?: - ( (?: 0 | [ 1 - 9 ] \d * | \d * [ a - z A - Z - ] [ 0 - 9 a - z A - Z - ] * ) (?: \. (?: 0 | [ 1 - 9 ] \d * | \d * [ a - z A - Z - ] [ 0 - 9 a - z A - Z - ] * ) ) * ) ) ? (?: \+ ( [ 0 - 9 a - z A - Z - ] + (?: \. [ 0 - 9 a - z A - Z - ] + ) * ) ) ? $ / ;
14- const reVersionPrefix = / ^ v / ;
15- const reVerToken = / _ V E R _ / g;
16- const reMajorToken = / _ M A J O R _ / g;
17- const reMinorToken = / _ M I N O R _ / g;
18- const rePatchToken = / _ P A T C H _ / g;
19- const reMajorVersion = / ( [ 0 - 9 ] + ) \. [ 0 - 9 ] + \. [ 0 - 9 ] + ( .* ) / ;
20- const reMinorVersion = / ( [ 0 - 9 ] + \. ) ( [ 0 - 9 ] + ) \. [ 0 - 9 ] + ( .* ) / ;
21- const rePatchVersion = / ( [ 0 - 9 ] + \. [ 0 - 9 ] + \. ) ( [ 0 - 9 ] + ) ( .* ) / ;
2214const rePrereleaseVersion = / ^ ( \d + ) \. ( \d + ) \. ( \d + ) (?: - ( [ a - z A - Z 0 - 9 - ] + (?: \. [ a - z A - Z 0 - 9 - ] + ) * ) ) ? / ;
2315const rePrereleaseIdNum = / ^ ( [ a - z A - Z 0 - 9 - ] + ) \. ( \d + ) $ / ;
2416const reDatePattern = / ( [ ^ 0 - 9 ] | ^ ) [ 0 - 9 ] { 4 } - [ 0 - 9 ] { 2 } - [ 0 - 9 ] { 2 } ( [ ^ 0 - 9 ] | $ ) / g;
2517const reReplaceString = / ^ s # ( [ ^ # ] + ) # ( [ ^ # ] + ) # ( .* ) $ / ;
2618
19+ function stripV ( str : string ) : string {
20+ return str [ 0 ] === "v" ? str . slice ( 1 ) : str ;
21+ }
22+
2723export function esc ( str : string ) : string {
2824 return str . replace ( reEscapeChars , "\\$&" ) ;
2925}
3026
3127export function isSemver ( str : string ) : boolean {
32- return reSemver . test ( str . replace ( reVersionPrefix , "" ) ) ;
28+ return reSemver . test ( stripV ( str ) ) ;
3329}
3430
3531export function replaceTokens ( str : string , newVersion : string ) : string {
3632 const [ major , minor , patch ] = newVersion . split ( "." ) ;
3733 return str
38- . replace ( reVerToken , newVersion )
39- . replace ( reMajorToken , major )
40- . replace ( reMinorToken , minor )
41- . replace ( rePatchToken , patch ) ;
34+ . replaceAll ( "_VER_" , newVersion )
35+ . replaceAll ( "_MAJOR_" , major )
36+ . replaceAll ( "_MINOR_" , minor )
37+ . replaceAll ( "_PATCH_" , patch ) ;
4238}
4339
4440export function incrementSemver ( str : string , level : string , preid ?: string ) : string {
4541 if ( ! isSemver ( str ) ) throw new Error ( `Invalid semver: ${ str } ` ) ;
46- if ( level === "major" ) {
47- const newVer = str . replace ( reMajorVersion , ( _ , m1 ) => `${ Number ( m1 ) + 1 } .0.0` ) ;
48- return preid ? `${ newVer } -${ preid } .0` : newVer ;
49- }
50- if ( level === "minor" ) {
51- const newVer = str . replace ( reMinorVersion , ( _ , m1 , m2 ) => `${ m1 } ${ Number ( m2 ) + 1 } .0` ) ;
52- return preid ? `${ newVer } -${ preid } .0` : newVer ;
53- }
54- if ( level === "patch" ) {
55- const newVer = str . replace ( rePatchVersion , ( _ , m1 , m2 ) => `${ m1 } ${ Number ( m2 ) + 1 } ` ) ;
56- return preid ? `${ newVer } -${ preid } .0` : newVer ;
57- }
42+ const [ , majStr , minStr , patStr , prerelease ] = rePrereleaseVersion . exec ( stripV ( str ) ) ! ;
43+ const major = Number ( majStr ) , minor = Number ( minStr ) , patch = Number ( patStr ) ;
44+ const tail = preid ? `-${ preid } .0` : "" ;
45+
46+ if ( level === "major" ) return `${ major + 1 } .0.0${ tail } ` ;
47+ if ( level === "minor" ) return `${ major } .${ minor + 1 } .0${ tail } ` ;
48+ if ( level === "patch" ) return `${ major } .${ minor } .${ patch + 1 } ${ tail } ` ;
5849 if ( level === "prerelease" ) {
5950 if ( ! preid ) throw new Error ( "prerelease requires --preid option" ) ;
60-
61- // Check if current version has a prerelease
62- const match = rePrereleaseVersion . exec ( str ) ;
63- if ( ! match ) throw new Error ( `Invalid semver: ${ str } ` ) ;
64-
65- const [ , major , minor , patch , prerelease ] = match ;
66-
67- if ( ! prerelease ) {
68- // No prerelease, increment patch and add prerelease
69- return `${ major } .${ minor } .${ Number ( patch ) + 1 } -${ preid } .0` ;
70- }
71-
72- // Has prerelease, check if it matches the requested preid
73- const prereleaseMatch = rePrereleaseIdNum . exec ( prerelease ) ;
74- if ( prereleaseMatch ) {
75- const [ , currentPreid , preNum ] = prereleaseMatch ;
76- if ( currentPreid === preid ) {
77- // Same preid, increment the number
78- return `${ major } .${ minor } .${ patch } -${ preid } .${ Number ( preNum ) + 1 } ` ;
79- }
51+ if ( ! prerelease ) return `${ major } .${ minor } .${ patch + 1 } -${ preid } .0` ;
52+ const idNum = rePrereleaseIdNum . exec ( prerelease ) ;
53+ if ( idNum ?. [ 1 ] === preid ) {
54+ return `${major } . $ { minor } . ${patch } - $ { preid } . $ { Number ( idNum [ 2 ] ) + 1 } `;
8055 }
81-
82- // Different preid or no number, replace with new preid
8356 return ` $ { major } . ${minor } . ${patch } - $ { preid } .0 `;
8457 }
8558 throw new Error(` Invalid semver level: $ { level } `);
8659}
8760
8861export function findUp(filename: string, dir: string, stopDir?: string): string | null {
89- const path = join ( dir , filename ) ;
90-
91- try {
92- accessSync ( path ) ;
93- return path ;
94- } catch { }
95-
96- const parent = dirname ( dir ) ;
97- if ( ( stopDir && path === stopDir ) || parent === dir ) {
98- return null ;
99- } else {
100- return findUp ( filename , parent , stopDir ) ;
62+ while (true) {
63+ const path = join(dir, filename);
64+ try {
65+ accessSync(path);
66+ return path;
67+ } catch {}
68+ const parent = dirname(dir);
69+ if ((stopDir && path === stopDir) || parent === dir) return null;
70+ dir = parent;
10171 }
10272}
10373
@@ -109,7 +79,7 @@ export function readVersionFromPackageJson(projectRoot: string): string | null {
10979 const content = readFileSync(packageJsonPath, "utf8");
11080 const pkg = JSON.parse(content);
11181 if (pkg.version && isSemver(pkg.version)) {
112- return pkg . version . replace ( reVersionPrefix , "" ) ;
82+ return stripV( pkg.version);
11383 }
11484 } catch {}
11585
@@ -124,11 +94,11 @@ export function readVersionFromPyprojectToml(projectRoot: string): string | null
12494 const content = readFileSync(pyprojectPath, "utf8");
12595 const projectVersion = tomlGetString(content, "project", "version");
12696 if (projectVersion && isSemver(projectVersion)) {
127- return projectVersion . replace ( reVersionPrefix , "" ) ;
97+ return stripV(projectVersion );
12898 }
12999 const poetryVersion = tomlGetString(content, "tool.poetry", "version");
130100 if (poetryVersion && isSemver(poetryVersion)) {
131- return poetryVersion . replace ( reVersionPrefix , "" ) ;
101+ return stripV(poetryVersion );
132102 }
133103 } catch {}
134104
@@ -206,7 +176,7 @@ export function getFileChanges({file, baseVersion, newVersion, replacements, dat
206176}
207177
208178export function write(file: string, content: string): void {
209- if ( platform ( ) === "win32" ) {
179+ if (platform === "win32") {
210180 try {
211181 truncateSync(file);
212182 writeFileSync(file, content, {flag: "r+"});
@@ -226,7 +196,7 @@ export function joinStrings(strings: Array<string | undefined>, separator: strin
226196function end(err?: Error | string | void): void {
227197 if (err) {
228198 console.info(err instanceof SubprocessError ? ` $ { err . message } \n${err . output } ` :
229- err instanceof Error ? String ( err . stack || err . message || err ) . trim ( ) :
199+ err instanceof Error ? (err.stack || err.message).trim() :
230200 err);
231201 }
232202 exit(err ? 1 : 0);
@@ -447,7 +417,7 @@ async function main(): Promise<void> {
447417 const result = await exec ( "git" , [ "describe" , "--tags" , "--abbrev=0" ] ) ;
448418 cachedDescribeTag = result . stdout . trim ( ) ;
449419 if ( isSemver ( cachedDescribeTag ) ) {
450- baseVersion = cachedDescribeTag . replace ( reVersionPrefix , "" ) ;
420+ baseVersion = stripV ( cachedDescribeTag ) ;
451421 baseSource = "git describe" ;
452422 }
453423 } catch { }
@@ -459,7 +429,7 @@ async function main(): Promise<void> {
459429 } catch { }
460430 const tag = stdout . split ( reNewline ) . map ( v => v . trim ( ) ) . find ( t => t && isSemver ( t ) ) ;
461431 if ( tag ) {
462- baseVersion = tag . replace ( reVersionPrefix , "" ) ;
432+ baseVersion = stripV ( tag ) ;
463433 baseSource = "git tag list" ;
464434 }
465435 }
@@ -493,7 +463,7 @@ async function main(): Promise<void> {
493463 }
494464 logVerbose ( `base version ${ baseVersion } from ${ baseSource } ` ) ;
495465
496- if ( baseVersion . startsWith ( "v" ) ) baseVersion = baseVersion . substring ( 1 ) ;
466+ baseVersion = stripV ( baseVersion ) ;
497467 if ( ! isSemver ( baseVersion ) ) {
498468 throw new Error ( `Invalid base version: ${ baseVersion } ` ) ;
499469 }
0 commit comments