@@ -9,38 +9,44 @@ import { OutputHelper } from '@quenty/cli-output-helpers';
99import { readFile , writeFile } from 'fs/promises' ;
1010import { Memoize } from 'typescript-memoize' ;
1111
12- const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000 ; // 24 hours
12+ const CHECK_INTERVAL_MS = 60 * 60 * 1000 ; // 1 hour
1313
1414interface VersionCache {
1515 lastCheck : number ;
1616 latestVersion : string ;
1717 currentVersion : string ;
18+ isLocalDev : boolean ;
1819}
1920
2021interface UpdateCheckResult {
2122 updateAvailable : boolean ;
2223 currentVersion : string ;
2324 latestVersion : string ;
25+ isLocalDev : boolean ;
2426}
2527
2628interface VersionCheckerOptions {
2729 packageName : string ;
2830 humanReadableName ?: string ;
2931 registryUrl : string ;
30- currentVersion ?: string ;
3132 packageJsonPath ?: string ;
3233 updateCommand ?: string ;
3334 verbose ?: boolean ;
3435}
3536
37+ interface OurVersionData {
38+ isLocalDev : boolean ;
39+ version : string ;
40+ }
41+
3642export class VersionChecker {
3743 public static async checkForUpdatesAsync (
3844 options : VersionCheckerOptions
3945 ) : Promise < UpdateCheckResult | undefined > {
4046 try {
4147 return await VersionChecker . _checkForUpdatesInternalAsync ( options ) ;
4248 } catch ( error ) {
43- const name = options . humanReadableName || options . packageName ;
49+ const name = VersionChecker . _getDisplayName ( options ) ;
4450 OutputHelper . box (
4551 `Failed to check for updates for ${ name } due to ${ error } `
4652 ) ;
@@ -55,40 +61,57 @@ export class VersionChecker {
5561 const {
5662 packageName,
5763 registryUrl,
58- currentVersion,
5964 packageJsonPath,
6065 updateCommand = `npm install -g ${ packageName } @latest` ,
6166 } = options ;
6267
63- const version = await VersionChecker . _queryOurVersionAsync (
64- currentVersion ,
68+ const versionData = await VersionChecker . _queryOurVersionAsync (
6569 packageJsonPath
6670 ) ;
67- if ( ! version ) {
71+ if ( ! versionData ) {
6872 if ( options . verbose ) {
73+ const name = VersionChecker . _getDisplayName ( options ) ;
6974 OutputHelper . error (
70- `Could not determine current version for ${ packageName } , skipping update check.`
75+ `Could not determine current version for ${ name } , skipping update check.`
7176 ) ;
7277 }
7378 return undefined ;
7479 }
7580
7681 const result = await VersionChecker . _queryUpdateStateAsync (
7782 packageName ,
78- version ,
83+ versionData ,
7984 registryUrl
8085 ) ;
8186
8287 if ( options . verbose ) {
88+ const currentyVersionDisplayName =
89+ VersionChecker . _getLocalVersionDisplayName ( versionData ) ;
90+
8391 OutputHelper . info (
84- `Checked for updates for ${ packageName } . Current version: ${ result . currentVersion } , Latest version: ${ result . latestVersion } , and update available: ${ result . updateAvailable } `
92+ `Checked for updates for ${ packageName } . Current version: ${ currentyVersionDisplayName } , Latest version: ${ result . latestVersion } , and update available: ${ result . updateAvailable } `
8593 ) ;
8694 }
8795
88- if ( result . updateAvailable ) {
89- const name = options . humanReadableName || packageName ;
96+ if ( result . isLocalDev ) {
97+ const name = VersionChecker . _getDisplayName ( options ) ;
98+ const text = [
99+ `${ name } is running in local development mode` ,
100+ '' ,
101+ OutputHelper . formatHint (
102+ `Run '${ updateCommand } ' to switch to production copy`
103+ ) ,
104+ '' ,
105+ 'This will result in less errors.' ,
106+ ] . join ( '\n' ) ;
107+
108+ OutputHelper . box ( text , { centered : true } ) ;
109+ } else if ( result . updateAvailable ) {
110+ const name = VersionChecker . _getDisplayName ( options ) ;
111+ const currentyVersionDisplayName =
112+ VersionChecker . _getLocalVersionDisplayName ( versionData ) ;
90113 const text = [
91- `${ name } update available: ${ result . currentVersion } → ${ result . latestVersion } ` ,
114+ `${ name } update available: ${ currentyVersionDisplayName } → ${ result . latestVersion } ` ,
92115 '' ,
93116 OutputHelper . formatHint ( `Run '${ updateCommand } ' to update` ) ,
94117 ] . join ( '\n' ) ;
@@ -99,28 +122,70 @@ export class VersionChecker {
99122 return result ;
100123 }
101124
125+ private static _getDisplayName ( options : VersionCheckerOptions ) : string {
126+ return options . humanReadableName || options . packageName ;
127+ }
128+
129+ /**
130+ * Helper method to get version display name from UpdateCheckResult
131+ */
132+ public static getVersionDisplayName ( versionData : UpdateCheckResult ) : string {
133+ return VersionChecker . _getLocalVersionDisplayName ( {
134+ isLocalDev : versionData . isLocalDev ,
135+ version : versionData . currentVersion ,
136+ } ) ;
137+ }
138+
139+ private static _getLocalVersionDisplayName (
140+ versionData : OurVersionData
141+ ) : string {
142+ return versionData . isLocalDev
143+ ? `${ versionData . version } -local-copy`
144+ : versionData . version ;
145+ }
146+
102147 @Memoize ( )
103148 private static async _queryOurVersionAsync (
104- currentVersion : string | undefined ,
105149 packageJsonPath : string | undefined
106- ) : Promise < string | null > {
107- if ( currentVersion ) {
108- return currentVersion ;
109- }
110-
150+ ) : Promise < OurVersionData | null > {
111151 if ( ! packageJsonPath ) {
112152 throw new Error (
113153 'Either currentVersion or packageJsonPath must be provided to determine the current version.'
114154 ) ;
115155 }
116156
117157 const pkg = JSON . parse ( await readFile ( packageJsonPath , 'utf8' ) ) ;
118- return pkg . version || null ;
158+
159+ // Check dependencies and see if they're workspace* or link:* or file:* instead of a version
160+ function isLinkedDependencies (
161+ deps : Record < string , string > | undefined
162+ ) : boolean {
163+ if ( ! deps ) {
164+ return false ;
165+ }
166+
167+ return Object . values ( deps ) . some (
168+ ( dep ) =>
169+ dep . startsWith ( 'workspace:' ) ||
170+ dep . startsWith ( 'link:' ) ||
171+ dep . startsWith ( 'file:' )
172+ ) ;
173+ }
174+
175+ const isLocalDev =
176+ isLinkedDependencies ( pkg . dependencies ) ||
177+ isLinkedDependencies ( pkg . devDependencies ) ||
178+ isLinkedDependencies ( pkg . peerDependencies ) ;
179+
180+ return {
181+ isLocalDev : isLocalDev ,
182+ version : pkg . version || null ,
183+ } ;
119184 }
120185
121186 private static async _queryUpdateStateAsync (
122187 packageName : string ,
123- currentVersion : string ,
188+ versionData : OurVersionData ,
124189 registryUrl : string
125190 ) : Promise < UpdateCheckResult > {
126191 // Use a simple cache file in the user's home directory
@@ -145,12 +210,17 @@ export class VersionChecker {
145210 if (
146211 cachedData &&
147212 ( now - cachedData . lastCheck < CHECK_INTERVAL_MS ||
148- cachedData . currentVersion !== currentVersion )
213+ cachedData . currentVersion !== versionData . version ||
214+ cachedData . isLocalDev !== versionData . isLocalDev )
149215 ) {
150216 return {
151- updateAvailable : semver . gt ( cachedData . latestVersion , currentVersion ) ,
152- currentVersion : currentVersion ,
217+ updateAvailable : semver . gt (
218+ cachedData . latestVersion ,
219+ versionData . version
220+ ) ,
221+ currentVersion : versionData . version ,
153222 latestVersion : cachedData . latestVersion ,
223+ isLocalDev : versionData . isLocalDev ,
154224 } ;
155225 }
156226
@@ -165,7 +235,8 @@ export class VersionChecker {
165235 const newCache : VersionCache = {
166236 lastCheck : now ,
167237 latestVersion : latestVersionString ,
168- currentVersion : currentVersion ,
238+ currentVersion : versionData . version ,
239+ isLocalDev : versionData . isLocalDev ,
169240 } ;
170241 const newResults = loadedCacheData || { } ;
171242 newResults [ cacheKey ] = newCache ;
@@ -179,9 +250,10 @@ export class VersionChecker {
179250
180251 // Return whether update is available
181252 return {
182- updateAvailable : semver . gt ( latestVersionString , currentVersion ) ,
183- currentVersion : currentVersion ,
253+ updateAvailable : semver . gt ( latestVersionString , versionData . version ) ,
254+ currentVersion : versionData . version ,
184255 latestVersion : latestVersionString ,
256+ isLocalDev : versionData . isLocalDev ,
185257 } ;
186258 }
187259}
0 commit comments