1- import { get , getAllPackages , post , put , uploadFile , doDelete } from './api' ;
1+ import { doDelete , get , getAllPackages , post , put , uploadFile } from './api' ;
22import { question , saveToLocal } from './utils' ;
33import { t } from './utils/i18n' ;
44
55import chalk from 'chalk' ;
66import { satisfies } from 'compare-versions' ;
7+ import Table from 'tty-table' ;
78import { getPlatform , getSelectedApp } from './app' ;
89import { choosePackage } from './package' ;
910import type { Package , Platform , Version } from './types' ;
@@ -24,6 +25,258 @@ interface VersionCommandOptions {
2425 packageVersionRange ?: string ;
2526 rollout ?: string ;
2627 dryRun ?: boolean ;
28+ versionDeps ?: Record < string , string > ;
29+ }
30+
31+ type Deps = Record < string , string > ;
32+ type DepChangeType = 'added' | 'removed' | 'changed' ;
33+
34+ interface DepChange {
35+ dependency : string ;
36+ oldVersion : string ;
37+ newVersion : string ;
38+ type : DepChangeType ;
39+ }
40+
41+ interface DepsChangeSummary {
42+ added : number ;
43+ removed : number ;
44+ changed : number ;
45+ }
46+
47+ function normalizeDeps ( input : unknown ) : Deps | undefined {
48+ if ( ! input ) {
49+ return undefined ;
50+ }
51+
52+ let raw : unknown = input ;
53+ if ( typeof input === 'string' ) {
54+ try {
55+ raw = JSON . parse ( input ) ;
56+ } catch ( _e ) {
57+ return undefined ;
58+ }
59+ }
60+
61+ if ( ! raw || typeof raw !== 'object' || Array . isArray ( raw ) ) {
62+ return undefined ;
63+ }
64+
65+ const deps : Deps = { } ;
66+ for ( const [ name , version ] of Object . entries (
67+ raw as Record < string , unknown > ,
68+ ) ) {
69+ if ( typeof version === 'string' && version ) {
70+ deps [ name ] = version ;
71+ }
72+ }
73+
74+ return Object . keys ( deps ) . length > 0 ? deps : undefined ;
75+ }
76+
77+ function getDepsChanges ( oldDeps ?: Deps , newDeps ?: Deps ) : DepChange [ ] {
78+ if ( ! oldDeps || ! newDeps ) {
79+ return [ ] ;
80+ }
81+
82+ const rows : DepChange [ ] = [ ] ;
83+ const keys = Object . keys ( { ...oldDeps , ...newDeps } ) . sort ( ( a , b ) =>
84+ a . localeCompare ( b ) ,
85+ ) ;
86+
87+ for ( const key of keys ) {
88+ const oldVersion = oldDeps [ key ] ;
89+ const newVersion = newDeps [ key ] ;
90+
91+ if ( oldVersion === undefined && newVersion !== undefined ) {
92+ rows . push ( {
93+ dependency : key ,
94+ oldVersion : '-' ,
95+ newVersion,
96+ type : 'added' ,
97+ } ) ;
98+ continue ;
99+ }
100+
101+ if ( oldVersion !== undefined && newVersion === undefined ) {
102+ rows . push ( {
103+ dependency : key ,
104+ oldVersion,
105+ newVersion : '-' ,
106+ type : 'removed' ,
107+ } ) ;
108+ continue ;
109+ }
110+
111+ if (
112+ oldVersion !== undefined &&
113+ newVersion !== undefined &&
114+ oldVersion !== newVersion
115+ ) {
116+ rows . push ( {
117+ dependency : key ,
118+ oldVersion,
119+ newVersion,
120+ type : 'changed' ,
121+ } ) ;
122+ }
123+ }
124+
125+ return rows ;
126+ }
127+
128+ function getDepsChangeSummary ( changes : DepChange [ ] ) : DepsChangeSummary {
129+ return changes . reduce (
130+ ( acc , item ) => {
131+ if ( item . type === 'added' ) {
132+ acc . added += 1 ;
133+ } else if ( item . type === 'removed' ) {
134+ acc . removed += 1 ;
135+ } else {
136+ acc . changed += 1 ;
137+ }
138+ return acc ;
139+ } ,
140+ { added : 0 , removed : 0 , changed : 0 } ,
141+ ) ;
142+ }
143+
144+ function renderVersionChange ( change : DepChange ) {
145+ const arrow = chalk . gray ( ` ${ t ( 'depsChangeArrow' ) } ` ) ;
146+
147+ if ( change . type === 'added' ) {
148+ return `${ chalk . red ( t ( 'depsChangeAddedLabel' ) ) } | ${ chalk . gray (
149+ change . oldVersion ,
150+ ) } ${ arrow } ${ chalk . red ( change . newVersion ) } `;
151+ }
152+
153+ if ( change . type === 'removed' ) {
154+ return `${ chalk . green ( t ( 'depsChangeRemovedLabel' ) ) } | ${ chalk . green (
155+ change . oldVersion ,
156+ ) } ${ arrow } ${ chalk . gray ( change . newVersion ) } `;
157+ }
158+
159+ return `${ chalk . yellow ( t ( 'depsChangeChangedLabel' ) ) } | ${ chalk . yellow (
160+ change . oldVersion ,
161+ ) } ${ arrow } ${ chalk . yellow ( change . newVersion ) } `;
162+ }
163+
164+ function printDepsChangesForPackage ( {
165+ pkg,
166+ versionDeps,
167+ } : {
168+ pkg : Package ;
169+ versionDeps : Deps ;
170+ } ) {
171+ const pkgDeps = normalizeDeps ( pkg . deps ) ;
172+ if ( ! pkgDeps ) {
173+ return false ;
174+ }
175+
176+ const changes = getDepsChanges ( pkgDeps , versionDeps ) ;
177+ if ( changes . length === 0 ) {
178+ return false ;
179+ }
180+
181+ const summary = getDepsChangeSummary ( changes ) ;
182+ const summaryText = t ( 'depsChangeSummary' , {
183+ added : chalk . red ( String ( summary . added ) ) ,
184+ removed : chalk . green ( String ( summary . removed ) ) ,
185+ changed : chalk . yellow ( String ( summary . changed ) ) ,
186+ } ) ;
187+ const header = [
188+ { value : t ( 'depsChangeDependencyHeader' ) } ,
189+ { value : t ( 'depsChangeVersionHeader' ) } ,
190+ ] ;
191+ const rows = changes . map ( ( change ) => [
192+ change . dependency ,
193+ renderVersionChange ( change ) ,
194+ ] ) ;
195+
196+ console . log ( '' ) ;
197+ console . log (
198+ chalk . yellow (
199+ t ( 'depsChangeTargetPackage' , {
200+ packageName : pkg . name ,
201+ packageId : pkg . id ,
202+ } ) ,
203+ ) ,
204+ ) ;
205+ console . log ( summaryText ) ;
206+ console . log ( Table ( header , rows ) . render ( ) ) ;
207+ console . log ( chalk . yellow ( t ( 'depsChangeRiskWarning' ) ) ) ;
208+ return true ;
209+ }
210+
211+ async function findVersionDeps ( appId : string , versionId : string ) {
212+ const targetId = String ( versionId ) ;
213+ const limit = 100 ;
214+ let offset = 0 ;
215+
216+ while ( true ) {
217+ const { data, count } = await get (
218+ `/app/${ appId } /version/list?offset=${ offset } &limit=${ limit } ` ,
219+ ) ;
220+ const versions : Version [ ] = Array . isArray ( data ) ? data : [ ] ;
221+ const version = versions . find ( ( item ) => String ( item . id ) === targetId ) ;
222+
223+ if ( version ) {
224+ return normalizeDeps ( version . deps ) ;
225+ }
226+
227+ offset += versions . length ;
228+ if ( versions . length === 0 || offset >= Number ( count || 0 ) ) {
229+ break ;
230+ }
231+ }
232+
233+ return undefined ;
234+ }
235+
236+ async function printDepsChangesForPublish ( {
237+ appId,
238+ versionId,
239+ pkgs,
240+ providedVersionDeps,
241+ } : {
242+ appId : string ;
243+ versionId ?: string ;
244+ pkgs : Package [ ] ;
245+ providedVersionDeps ?: Deps ;
246+ } ) {
247+ if ( ! versionId || pkgs . length === 0 ) {
248+ return ;
249+ }
250+
251+ let versionDeps = normalizeDeps ( providedVersionDeps ) ;
252+ if ( ! versionDeps ) {
253+ try {
254+ versionDeps = await findVersionDeps ( appId , versionId ) ;
255+ } catch ( error : any ) {
256+ console . warn (
257+ chalk . yellow (
258+ t ( 'depsChangeFetchFailed' , {
259+ error : error ?. message || String ( error ) ,
260+ } ) ,
261+ ) ,
262+ ) ;
263+ return ;
264+ }
265+ }
266+
267+ if ( ! versionDeps ) {
268+ return ;
269+ }
270+
271+ let hasChanges = false ;
272+ for ( const pkg of pkgs ) {
273+ const printed = printDepsChangesForPackage ( { pkg, versionDeps } ) ;
274+ hasChanges = hasChanges || printed ;
275+ }
276+
277+ if ( hasChanges ) {
278+ console . log ( chalk . yellow ( t ( 'depsChangeNonBlockingHint' ) ) ) ;
279+ }
27280}
28281
29282async function showVersion ( appId : string , offset : number ) {
@@ -211,12 +464,15 @@ export const versionCommands = {
211464 maxPackageVersion,
212465 rollout,
213466 dryRun,
467+ versionDeps : depVersions ,
214468 } ,
215469 } ) ;
216470 } else {
217471 const q = await question ( t ( 'updateNativePackageQuestion' ) ) ;
218472 if ( q . toLowerCase ( ) === 'y' ) {
219- await this . update ( { options : { versionId : id , platform } } ) ;
473+ await this . update ( {
474+ options : { versionId : id , platform, versionDeps : depVersions } ,
475+ } ) ;
220476 }
221477 }
222478 return versionName ;
@@ -318,6 +574,13 @@ export const versionCommands = {
318574 }
319575 }
320576
577+ await printDepsChangesForPublish ( {
578+ appId,
579+ versionId,
580+ pkgs : pkgsToBind ,
581+ providedVersionDeps : options . versionDeps ,
582+ } ) ;
583+
321584 await bindVersionToPackages ( {
322585 appId,
323586 versionId,
0 commit comments