react-native-update-cli 1.44.2 → 1.44.5

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.
@@ -5,7 +5,7 @@ import pkg from '../../package.json';
5
5
  import AppInfoParser from './app-info-parser';
6
6
  import semverSatisfies from 'semver/functions/satisfies';
7
7
  import chalk from 'chalk';
8
- import latestVersion from '@badisi/latest-version';
8
+ import latestVersion from '../utils/latest-version';
9
9
  import { checkPlugins } from './check-plugin';
10
10
 
11
11
  import { read } from 'read';
@@ -98,7 +98,7 @@ export async function getAppInfo(fn: string) {
98
98
  const metaJsonFile = await appInfoParser.parser.getEntryFromHarmonyApp(
99
99
  /rawfile\/meta.json/,
100
100
  );
101
- let metaData = {};
101
+ let metaData: Record<string, any> = {};
102
102
  if (metaJsonFile) {
103
103
  metaData = JSON.parse(metaJsonFile.toString());
104
104
  }
@@ -181,16 +181,13 @@ export async function printVersionCommand() {
181
181
  version: chalk.green(latestRnuCliVersion),
182
182
  })}`
183
183
  : '';
184
- console.log(
185
- `react-native-update-cli: ${pkg.version}${latestRnuCliVersion}`,
186
- );
187
- let rnuVersion = '';
188
- rnuVersion = depVersions['react-native-update'];
189
- latestRnuVersion = latestRnuVersion
190
- ? ` ${t('latestVersionTag', { version: chalk.green(latestRnuVersion) })}`
191
- : '';
192
- console.log(`react-native-update: ${rnuVersion}${latestRnuVersion}`);
184
+ console.log(`react-native-update-cli: ${pkg.version}${latestRnuCliVersion}`);
185
+ const rnuVersion = depVersions['react-native-update'];
193
186
  if (rnuVersion) {
187
+ latestRnuVersion = latestRnuVersion
188
+ ? ` ${t('latestVersionTag', { version: chalk.green(latestRnuVersion) })}`
189
+ : '';
190
+ console.log(`react-native-update: ${rnuVersion}${latestRnuVersion}`);
194
191
  if (IS_CRESC) {
195
192
  if (semverSatisfies(rnuVersion, '<10.27.0')) {
196
193
  console.error(
@@ -0,0 +1,443 @@
1
+ import {
2
+ blue,
3
+ bold,
4
+ cyan,
5
+ gray,
6
+ green,
7
+ italic,
8
+ magenta,
9
+ red,
10
+ reset,
11
+ strip,
12
+ underline,
13
+ yellow,
14
+ } from '@colors/colors/safe';
15
+ import { existsSync, readFileSync } from 'node:fs';
16
+ import { dirname } from 'node:path';
17
+ import latestVersion, {
18
+ type Package,
19
+ type PackageJson,
20
+ type LatestVersionPackage,
21
+ type LatestVersionOptions,
22
+ } from '.';
23
+ import semverMajor from 'semver/functions/major';
24
+ import semverDiff from 'semver/functions/diff';
25
+
26
+ interface TableColumn {
27
+ label: string;
28
+ attrName: keyof TableRow;
29
+ align: 'left' | 'center' | 'right';
30
+ maxLength: number;
31
+ items: string[];
32
+ }
33
+
34
+ type TableRowGroup =
35
+ | 'patch'
36
+ | 'minor'
37
+ | 'major'
38
+ | 'majorVersionZero'
39
+ | 'unknown';
40
+
41
+ interface TableRow {
42
+ name: string;
43
+ location: string;
44
+ installed: string;
45
+ tagOrRange: string;
46
+ separator: string;
47
+ wanted: string;
48
+ latest: string;
49
+ group: TableRowGroup;
50
+ }
51
+
52
+ const colorizeDiff = (from: string, to: string): string => {
53
+ const toParts = to.split('.');
54
+
55
+ const diffIndex = from.split('.').findIndex((part, i) => part !== toParts[i]);
56
+ if (diffIndex !== -1) {
57
+ let color = magenta;
58
+ if (toParts[0] !== '0') {
59
+ color = diffIndex === 0 ? red : diffIndex === 1 ? cyan : green;
60
+ }
61
+ const start = toParts.slice(0, diffIndex).join('.');
62
+ const mid = diffIndex === 0 ? '' : '.';
63
+ const end = color(toParts.slice(diffIndex).join('.'));
64
+ return `${start}${mid}${end}`;
65
+ }
66
+ return to;
67
+ };
68
+
69
+ const columnCellRenderer = (column: TableColumn, row: TableRow): string => {
70
+ let text = row[column.attrName];
71
+ const gap =
72
+ text.length < column.maxLength
73
+ ? ' '.repeat(column.maxLength - text.length)
74
+ : '';
75
+
76
+ switch (column.attrName) {
77
+ case 'name':
78
+ text = yellow(text);
79
+ break;
80
+ case 'installed':
81
+ case 'separator':
82
+ text = blue(text);
83
+ break;
84
+ case 'location':
85
+ case 'tagOrRange':
86
+ text = gray(text);
87
+ break;
88
+ case 'wanted':
89
+ text = colorizeDiff(row.installed, text);
90
+ break;
91
+ case 'latest':
92
+ if (text !== row.wanted) {
93
+ text = colorizeDiff(row.installed, text);
94
+ }
95
+ break;
96
+ default:
97
+ break;
98
+ }
99
+
100
+ return column.align === 'right' ? `${gap}${text}` : `${text}${gap}`;
101
+ };
102
+
103
+ const columnHeaderRenderer = (column: TableColumn): string => {
104
+ const text = column.label;
105
+ const gap =
106
+ text.length < column.maxLength
107
+ ? ' '.repeat(column.maxLength - text.length)
108
+ : '';
109
+ return column.align === 'right'
110
+ ? `${gap}${underline(text)}`
111
+ : `${underline(text)}${gap}`;
112
+ };
113
+
114
+ const drawBox = (
115
+ lines: string[],
116
+ color = yellow,
117
+ horizontalPadding = 3,
118
+ ): void => {
119
+ const maxLineWidth = lines.reduce(
120
+ (max, row) => Math.max(max, strip(row).length),
121
+ 0,
122
+ );
123
+
124
+ console.log(color(`┌${'─'.repeat(maxLineWidth + horizontalPadding * 2)}┐`));
125
+ lines.forEach((row) => {
126
+ const padding = ' '.repeat(horizontalPadding);
127
+ const fullRow = `${row}${' '.repeat(maxLineWidth - strip(row).length)}`;
128
+ console.log(
129
+ `${color('│')}${padding}${reset(fullRow)}${padding}${color('│')}`,
130
+ );
131
+ });
132
+ console.log(color(`└${'─'.repeat(maxLineWidth + horizontalPadding * 2)}┘`));
133
+ };
134
+
135
+ const getTableColumns = (rows: TableRow[]): TableColumn[] => {
136
+ const columns: TableColumn[] = [
137
+ {
138
+ label: 'Package',
139
+ attrName: 'name',
140
+ align: 'left',
141
+ maxLength: 0,
142
+ items: [],
143
+ },
144
+ {
145
+ label: 'Location',
146
+ attrName: 'location',
147
+ align: 'left',
148
+ maxLength: 0,
149
+ items: [],
150
+ },
151
+ {
152
+ label: 'Installed',
153
+ attrName: 'installed',
154
+ align: 'right',
155
+ maxLength: 0,
156
+ items: [],
157
+ },
158
+ {
159
+ label: '',
160
+ attrName: 'separator',
161
+ align: 'center',
162
+ maxLength: 0,
163
+ items: [],
164
+ },
165
+ {
166
+ label: 'Range',
167
+ attrName: 'tagOrRange',
168
+ align: 'right',
169
+ maxLength: 0,
170
+ items: [],
171
+ },
172
+ {
173
+ label: '',
174
+ attrName: 'separator',
175
+ align: 'center',
176
+ maxLength: 0,
177
+ items: [],
178
+ },
179
+ {
180
+ label: 'Wanted',
181
+ attrName: 'wanted',
182
+ align: 'right',
183
+ maxLength: 0,
184
+ items: [],
185
+ },
186
+ {
187
+ label: 'Latest',
188
+ attrName: 'latest',
189
+ align: 'right',
190
+ maxLength: 0,
191
+ items: [],
192
+ },
193
+ ];
194
+ rows.forEach((row) => {
195
+ columns.forEach((column) => {
196
+ column.maxLength = Math.max(
197
+ column.label.length,
198
+ column.maxLength,
199
+ row[column.attrName].length || 0,
200
+ );
201
+ });
202
+ });
203
+ return columns;
204
+ };
205
+
206
+ const getTableRows = (updates: LatestVersionPackage[]): TableRow[] => {
207
+ return updates.reduce<TableRow[]>((all, pkg) => {
208
+ const {
209
+ name,
210
+ latest,
211
+ local,
212
+ globalNpm,
213
+ globalYarn,
214
+ wantedTagOrRange,
215
+ updatesAvailable,
216
+ } = pkg;
217
+ const getGroup = (a?: string, b?: string): TableRowGroup => {
218
+ if (b && semverMajor(b) === 0) {
219
+ return 'majorVersionZero';
220
+ } else if (a && b) {
221
+ const releaseType = semverDiff(a, b) ?? '';
222
+ if (['major', 'premajor', 'prerelease'].includes(releaseType)) {
223
+ return 'major';
224
+ } else if (['minor', 'preminor'].includes(releaseType)) {
225
+ return 'minor';
226
+ } else if (['patch', 'prepatch'].includes(releaseType)) {
227
+ return 'patch';
228
+ }
229
+ }
230
+ return 'unknown';
231
+ };
232
+ const add = (
233
+ group: TableRowGroup,
234
+ location: string,
235
+ installed?: string,
236
+ wanted?: string,
237
+ ) =>
238
+ all.push({
239
+ name: ' ' + name,
240
+ location,
241
+ installed: installed ?? 'unknown',
242
+ latest: latest ?? 'unknown',
243
+ tagOrRange: wantedTagOrRange ?? 'unknown',
244
+ separator: '→',
245
+ wanted: wanted ?? 'unknown',
246
+ group,
247
+ });
248
+ if (updatesAvailable) {
249
+ if (updatesAvailable.local) {
250
+ add(
251
+ getGroup(local, updatesAvailable.local),
252
+ 'local',
253
+ local,
254
+ updatesAvailable.local,
255
+ );
256
+ }
257
+ if (updatesAvailable.globalNpm) {
258
+ add(
259
+ getGroup(globalNpm, updatesAvailable.globalNpm),
260
+ 'NPM global',
261
+ globalNpm,
262
+ updatesAvailable.globalNpm,
263
+ );
264
+ }
265
+ if (updatesAvailable.globalYarn) {
266
+ add(
267
+ getGroup(globalYarn, updatesAvailable.globalYarn),
268
+ 'YARN global',
269
+ globalYarn,
270
+ updatesAvailable.globalYarn,
271
+ );
272
+ }
273
+ } else {
274
+ if (local && local !== latest) {
275
+ add(getGroup(local, latest), 'local', local, pkg.wanted);
276
+ }
277
+ if (globalNpm && globalNpm !== latest) {
278
+ add(getGroup(globalNpm, latest), 'NPM global', globalNpm, pkg.wanted);
279
+ }
280
+ if (globalYarn && globalYarn !== latest) {
281
+ add(
282
+ getGroup(globalYarn, latest),
283
+ 'YARN global',
284
+ globalYarn,
285
+ pkg.wanted,
286
+ );
287
+ }
288
+ if (!local && !globalNpm && !globalYarn) {
289
+ add('unknown', 'unknown', 'unknown', pkg.wanted);
290
+ }
291
+ }
292
+ return all;
293
+ }, []);
294
+ };
295
+
296
+ const displayTable = (latestVersionPackages: LatestVersionPackage[]): void => {
297
+ const updates = latestVersionPackages.filter((pkg) => pkg.updatesAvailable);
298
+ if (updates.length) {
299
+ const rows = getTableRows(updates);
300
+ const hasUpdates = rows.some((row) => row.installed !== 'unknown');
301
+ const columns = getTableColumns(rows);
302
+ const columnGap = 2;
303
+
304
+ const getGroupLines = (
305
+ groupType: TableRowGroup,
306
+ color: (str: string) => string,
307
+ title: string,
308
+ description?: string,
309
+ ): string[] => {
310
+ const items = rows
311
+ .filter((row) => row.group === groupType)
312
+ .sort((a, b) => (a.name > b.name ? 1 : -1));
313
+ return !items.length
314
+ ? []
315
+ : [
316
+ '',
317
+ color(`${bold(title)} ${italic(`(${description})`)}`),
318
+ ...items.map((row) =>
319
+ columns
320
+ .map((column) => columnCellRenderer(column, row))
321
+ .join(' '.repeat(columnGap)),
322
+ ),
323
+ ];
324
+ };
325
+
326
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
327
+ drawBox(
328
+ [
329
+ '',
330
+ hasUpdates ? yellow('Important updates are available.') : undefined,
331
+ hasUpdates ? '' : undefined,
332
+ columns.map(columnHeaderRenderer).join(' '.repeat(columnGap)),
333
+ ...getGroupLines(
334
+ 'patch',
335
+ green,
336
+ 'Patch',
337
+ 'backwards-compatible bug fixes',
338
+ ),
339
+ ...getGroupLines(
340
+ 'minor',
341
+ cyan,
342
+ 'Minor',
343
+ 'backwards-compatible features',
344
+ ),
345
+ ...getGroupLines(
346
+ 'major',
347
+ red,
348
+ 'Major',
349
+ 'potentially breaking API changes',
350
+ ),
351
+ ...getGroupLines(
352
+ 'majorVersionZero',
353
+ magenta,
354
+ 'Major version zero',
355
+ 'not stable, anything may change',
356
+ ),
357
+ ...getGroupLines('unknown', blue, 'Missing', 'not installed'),
358
+ '',
359
+ ].filter((line) => line !== undefined) as string[],
360
+ );
361
+ } else {
362
+ console.log(green('🎉 Packages are up-to-date'));
363
+ }
364
+ };
365
+
366
+ const checkVersions = async (
367
+ packages: Package | Package[] | PackageJson,
368
+ skipMissing: boolean,
369
+ options: LatestVersionOptions = { useCache: true },
370
+ ): Promise<void> => {
371
+ const ora = (await import('ora')).default;
372
+ const spinner = ora({ text: cyan('Checking versions...') });
373
+ spinner.start();
374
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
375
+ // @ts-ignore
376
+ let latestVersionPackages: LatestVersionPackage[] = await latestVersion(
377
+ packages,
378
+ options,
379
+ );
380
+ if (skipMissing) {
381
+ latestVersionPackages = latestVersionPackages.filter(
382
+ (pkg) => pkg.local ?? pkg.globalNpm ?? pkg.globalYarn,
383
+ );
384
+ }
385
+ spinner.stop();
386
+ displayTable(latestVersionPackages);
387
+ };
388
+
389
+ void (async () => {
390
+ let args = process.argv.slice(2);
391
+
392
+ const skipMissing = args.includes('--skip-missing');
393
+
394
+ // Remove any options from the arguments
395
+ args = args.filter((arg) => !arg.startsWith('-'));
396
+
397
+ // If argument is a package.json file
398
+ if (args.length === 1 && args[0].endsWith('package.json')) {
399
+ if (existsSync(args[0])) {
400
+ process.chdir(dirname(args[0]));
401
+ await checkVersions(
402
+ JSON.parse(readFileSync(args[0]).toString()) as PackageJson,
403
+ skipMissing,
404
+ );
405
+ } else {
406
+ console.log(cyan('No package.json file were found'));
407
+ }
408
+ }
409
+ // else..
410
+ else {
411
+ // Check if a local package.json file exists
412
+ let localPkgJson: PackageJson | undefined;
413
+ if (existsSync('package.json')) {
414
+ localPkgJson = JSON.parse(readFileSync('package.json').toString());
415
+ }
416
+
417
+ // Check given arguments
418
+ if (args.length) {
419
+ // Map arguments with any range that could be found in local package.json
420
+ args = args.map((arg) => {
421
+ if (localPkgJson?.dependencies?.[arg]) {
422
+ return `${arg}@${localPkgJson.dependencies?.[arg]}`;
423
+ }
424
+ if (localPkgJson?.devDependencies?.[arg]) {
425
+ return `${arg}@${localPkgJson.devDependencies?.[arg]}`;
426
+ }
427
+ if (localPkgJson?.peerDependencies?.[arg]) {
428
+ return `${arg}@${localPkgJson.peerDependencies?.[arg]}`;
429
+ }
430
+ return arg;
431
+ });
432
+ await checkVersions(args, skipMissing);
433
+ }
434
+ // ...else check the local package.json if any
435
+ else if (localPkgJson) {
436
+ await checkVersions(localPkgJson, skipMissing);
437
+ }
438
+ // ...else do nothing
439
+ else {
440
+ console.log(cyan('No packages were found'));
441
+ }
442
+ }
443
+ })();