11/* eslint-disable no-console */
2- import { array , command , flag , multioption , option , string } from "cmd-ts" ;
2+ import { array , command , flag , multioption , option , string , Type } from "cmd-ts" ;
33import fs from "fs-extra" ;
44import path from "node:path" ;
55import { findUp } from "find-up" ;
@@ -9,6 +9,18 @@ import { SemVer } from "semver";
99import { exception2Result } from "@adviser/cement" ;
1010import { VersionPinner } from "./version-pinner.js" ;
1111
12+ export type VersionModifier = "~" | "^" | "" ;
13+ const allowedVersionModifiers : VersionModifier [ ] = [ "~" , "^" , "" ] ;
14+
15+ const versionModifier : Type < string , VersionModifier > = {
16+ async from ( str ) {
17+ if ( allowedVersionModifiers . includes ( str as VersionModifier ) ) {
18+ return str as VersionModifier ;
19+ }
20+ throw new Error ( `Invalid version modifier: "${ str } ". Must be one of: "~", "^", or ""` ) ;
21+ } ,
22+ } ;
23+
1224const reVersionAlphaStart = / ^ [ a - z ] ( \d + \. \d + \. \d + .* ) $ / ;
1325// const reVersionOptionalAlphaStart = /^[a-z]?(\d+\.\d+\.\d+.*)$/;
1426const reScopedVersion = / ^ [ ^ @ ] + @ ( .* ) $ / ;
@@ -112,29 +124,34 @@ export interface PackageJson {
112124 devDependencies : Record < string , string > ;
113125}
114126
127+ export interface PatchPackageJsonOptions {
128+ changeScope ?: string ;
129+ readonly mock ?: {
130+ readonly readJSON : typeof fs . readJson ;
131+ } ;
132+ }
133+
115134export async function patchPackageJson (
116135 packageJsonPath : string ,
117136 version : Version ,
118- changeScope ?: string ,
119- mock : {
120- readJSON : typeof fs . readJson ;
121- } = { readJSON : fs . readJson } ,
137+ opts : PatchPackageJsonOptions = { } ,
122138) : Promise < {
123139 patchedPackageJson : PackageJson ;
124140 originalPackageJson : PackageJson ;
125141} > {
126- const originalPackageJson = await mock . readJSON ( packageJsonPath ) ;
127- const patchedPackageJson = await mock . readJSON ( packageJsonPath ) ;
142+ const mock = opts . mock ?? { readJSON : fs . readJson } ;
143+
144+ const originalPackageJson = ( await mock . readJSON ( packageJsonPath ) ) as PackageJson ;
145+ const patchedPackageJson = ( await mock . readJSON ( packageJsonPath ) ) as PackageJson ;
128146 // ugly double read but this is easier than deep copying
129- if ( changeScope ) {
130- changeScope = changeScope . replace ( / ^ @ / , "" ) ;
147+ if ( opts . changeScope ) {
148+ opts . changeScope = opts . changeScope . replace ( / ^ @ / , "" ) ;
131149 if ( originalPackageJson . name . startsWith ( `@` ) ) {
132- patchedPackageJson . name = patchedPackageJson . name . replace ( / ^ @ [ ^ / ] + \/ / , `@${ changeScope } /` ) ;
150+ patchedPackageJson . name = patchedPackageJson . name . replace ( / ^ @ [ ^ / ] + \/ / , `@${ opts . changeScope } /` ) ;
133151 } else {
134- patchedPackageJson . name = `@${ changeScope } /${ patchedPackageJson . name } ` ;
152+ patchedPackageJson . name = `@${ opts . changeScope } /${ patchedPackageJson . name } ` ;
135153 }
136154 }
137- // patchedPackageJson.name = changeScope ? : patchedPackageJson.name;
138155 patchedPackageJson . version = version . version ;
139156 delete patchedPackageJson . scripts [ "pack" ] ;
140157 delete patchedPackageJson . scripts [ "publish" ] ;
@@ -359,6 +376,16 @@ export function buildCmd(sthis: SuperThis) {
359376 short : "t" ,
360377 description : "Do not update tsconfig.json in the destination directory." ,
361378 } ) ,
379+ patchVersionModify : option ( {
380+ long : "patchVersionModify" ,
381+ description : "Modify patch versions in package.json dependencies. Must be one of: ~, ^, or empty string." ,
382+ type : versionModifier ,
383+ defaultValue : ( ) => "~" as VersionModifier ,
384+ } ) ,
385+ noPatchedVersionModify : flag ( {
386+ long : "noPatchedVersionModify" ,
387+ description : "Do not modify patch versions in package.json dependencies." ,
388+ } ) ,
362389 noPinned : flag ( {
363390 long : "no-pinned" ,
364391 description : "Do not pin dependencies in package.json (pinning is enabled by default)." ,
@@ -519,7 +546,7 @@ export function buildCmd(sthis: SuperThis) {
519546 $ . verbose = true ;
520547 cd ( jsrDstDir ) ;
521548
522- let packageJson = await patchPackageJson ( "package.json" , version , args . changeScope ) ;
549+ let packageJson = await patchPackageJson ( "package.json" , version , { changeScope : args . changeScope } ) ;
523550 if ( ! args . noPinned ) {
524551 console . log (
525552 "Prepared package.json with pinning for" ,
@@ -533,6 +560,7 @@ export function buildCmd(sthis: SuperThis) {
533560 ...packageJson ,
534561 patchedPackageJson : pinner . pinVersions ( packageJson . patchedPackageJson , {
535562 workspaceVersion : version . prefixedVersion ,
563+ _3rdPartyVersionModifier : args . noPatchedVersionModify ? undefined : args . patchVersionModify ,
536564 } ) ,
537565 } ;
538566 }
@@ -605,6 +633,8 @@ export function buildCmd(sthis: SuperThis) {
605633 const semVer = new SemVer ( args . version ) ;
606634 if ( semVer . prerelease . find ( ( i ) => typeof i === "string" && i . includes ( "dev" ) ) ) {
607635 tags . push ( "dev" ) ;
636+ } else {
637+ tags . push ( "latest" ) ; // override to latest in prod
608638 }
609639 } catch ( e ) {
610640 console . warn ( `Warn parsing version ${ args . version } :` , e ) ;
0 commit comments