1- const result = {
2- integrity : '1234567890' ,
3- markdown : 'Hello from report.ts'
1+ import { exec , execSync , spawnSync , fork , spawn } from 'child_process' ;
2+ import * as path from 'path' ;
3+ import { promisify } from 'util' ;
4+
5+ const execp = promisify ( exec ) ;
6+ const spawnp = promisify ( spawn ) ;
7+
8+ interface YarnReport {
9+ Package : string ;
10+ Current : string ;
11+ Wanted : string ;
12+ Latest : string ;
13+ Workspace : string ;
14+ PackageType : string ;
15+ URL : string ;
416}
5- console . log ( JSON . stringify ( result ) ) ;
17+
18+ interface OutdatedPackage {
19+ url : string ;
20+ detectedVersions : {
21+ [ version : string ] : {
22+ projects : string [ ] ;
23+ } ;
24+ } ;
25+ availableUpdates : any ;
26+ }
27+
28+ interface OutdatedPackages {
29+ [ version : string ] : OutdatedPackage ;
30+ }
31+
32+ function mapToObject ( head : string [ ] , body : string [ ] [ ] ) : YarnReport [ ] {
33+ return body . map ( ( row ) => {
34+ return row . reduce ( ( acc , cell , index ) => {
35+ acc [ head [ index ] ] = cell ;
36+ return acc ;
37+ } , { } as YarnReport ) ;
38+ } ) ;
39+ }
40+
41+ function mapToPackage ( yarnReport : YarnReport [ ] ) : OutdatedPackages {
42+ return yarnReport . reduce ( ( acc , e ) => {
43+ const prevPkg = acc [ e . Package ] ;
44+ const pkg : OutdatedPackage = {
45+ url : `https://www.npmjs.com/package/${ e . Package } ` ,
46+ detectedVersions : {
47+ [ e . Current ] : {
48+ // projects: [e.Workspace, ...(prevPkg?.detectedVersions?.[e.Current]?.projects || [])].filter(
49+ // Boolean,
50+ // ),
51+ projects : [
52+ ...( prevPkg ?. detectedVersions ?. [ e . Current ] ?. projects || [ ] ) ,
53+ { name : e . Workspace , path : `//.../${ e . Workspace } /package.json` } ,
54+ ] ,
55+ } ,
56+ } ,
57+ availableUpdates : {
58+ // TODO: search from npm feed to gather more versions based on certain criteria, greatest than supported node versions + ts version, for greatest node + ts versions that arent suported in the sdk show a flag.
59+ // [e.Wanted]: {
60+ // },
61+ [ e . Latest ] : { type : 'CJS' , node : '>=18' } ,
62+ } ,
63+ } ;
64+
65+ acc [ e . Package ] = pkg ;
66+ return acc ;
67+ } , { } ) ;
68+ }
69+
70+ /**
71+ * Pluralize a word based on a count.
72+ *
73+ * @param n Count.
74+ * @param text Word to pluralize.
75+ * @param plural Plural suffix.
76+ * @returns Pluralized word.
77+ */
78+ function plural ( n : number , text : string , plural : string = 's' ) {
79+ return `${ text } ${ n === 1 ? '' : plural } ` ;
80+ }
81+
82+ function createDetectedVersionsMarkdown ( pkg : OutdatedPackage ) : string [ ] {
83+ const detectedVersions = Object . entries ( pkg . detectedVersions ) ;
84+ if ( detectedVersions . length === 0 ) {
85+ throw new Error ( 'No detected versions' ) ;
86+ }
87+
88+ let title = `- **detected ${ plural ( detectedVersions . length , 'version' ) } :**` ;
89+
90+ const isMultiVersion = detectedVersions . length > 1 ;
91+ const isSingleVersion = ! isMultiVersion ;
92+ const isSingleProject = detectedVersions [ 0 ] [ 1 ] . projects . length === 1 ;
93+
94+ if ( isSingleVersion && isSingleProject ) {
95+ title += ` _only one project contains the package._` ;
96+ } else if ( isSingleVersion ) {
97+ title += ` _multiple projects contain the same version of the package_.` ;
98+ } else if ( isMultiVersion ) {
99+ title += ` _multiple projects contain different versions of the package, please consolidate._` ;
100+ }
101+
102+ const lines = [ ] ;
103+ lines . push ( title ) ;
104+ for ( let i = 0 ; i < detectedVersions . length ; i ++ ) {
105+ const [ version , info ] = detectedVersions [ i ] ;
106+ const projects = info . projects . map ( ( e ) => `[\`${ e . name } \`](${ e . path } )` ) ;
107+ const line = ` ├─ [\`${ version } \`](${ pkg . url } /v/${ version } ) ─ **${ plural ( projects . length , 'project' ) } :** ${ projects . join ( '' ) } ` ;
108+ lines . push ( line ) ;
109+ }
110+ return lines ;
111+ }
112+
113+ function createAvailableUpdatesMarkdown ( pkg : OutdatedPackage ) : string [ ] {
114+ const availableUpdates = Object . entries ( pkg . availableUpdates ) ;
115+
116+ let title = `- **available ${ plural ( availableUpdates . length , 'update' ) } :**` ;
117+
118+ const isMultiVersion = availableUpdates . length > 1 ;
119+ const isSingleVersion = ! isMultiVersion ;
120+ const isSingleProject = availableUpdates [ 0 ] [ 1 ] . projects . length === 1 ;
121+
122+ if ( isSingleVersion && isSingleProject ) {
123+ title += ` _only one project contains the package._` ;
124+ } else if ( isSingleVersion ) {
125+ title += ` _multiple projects contain the same version of the package_.` ;
126+ } else if ( isMultiVersion ) {
127+ title += ` _multiple projects contain different versions of the package, please consolidate._` ;
128+ }
129+
130+ const lines = [ ] ;
131+ lines . push ( title ) ;
132+ for ( let i = 0 ; i < availableUpdates . length ; i ++ ) {
133+ const [ version , info ] = availableUpdates [ i ] ;
134+ const projects = info . projects . map ( ( e ) => `[\`${ e . name } \`](${ e . path } )` ) ;
135+ const line = ` ├─ [\`${ version } \`](${ pkg . url } /v/${ version } ) ─ **${ plural ( projects . length , 'project' ) } :** ${ projects . join ( '' ) } ` ;
136+ lines . push ( line ) ;
137+ }
138+ return lines ;
139+ }
140+
141+ function createMarkdown ( packages : OutdatedPackages ) {
142+ const lines = [ ] ;
143+ for ( const [ name , pkg ] of Object . entries ( packages ) ) {
144+ lines . push ( `## [${ name } ](${ pkg . url } )` ) ;
145+
146+ lines . concat ( createDetectedVersionsMarkdown ( pkg ) ) ;
147+ lines . concat ( createAvailableUpdatesMarkdown ( pkg ) ) ;
148+
149+ const availableUpdates = Object . entries ( pkg . availableUpdates ) ;
150+ title = `- **available ${ plural ( availableUpdates . length , 'updates' ) } :**` ;
151+ if ( detectedVersions . length > 1 ) {
152+ title += ' _newer version/s detected, please update to one of the following._' ;
153+ }
154+ }
155+ }
156+
157+ function main ( ) {
158+ const { stderr, stdout } = spawnSync ( 'yarn' , [ 'outdated' , '--json' ] , { encoding : 'utf-8' , shell : true } ) ;
159+
160+ if ( stderr . length > 0 && ! stderr . startsWith ( 'Debugger attached' ) ) {
161+ console . error ( stderr ) ;
162+ }
163+
164+ const info = stdout . split ( '\n' ) [ 1 ] ;
165+ const { head, body } = JSON . parse ( info ) . data ;
166+ const yarnReport = mapToObject ( head , body ) ;
167+ const outdatedPackages = mapToPackage ( yarnReport ) ;
168+ var markdown = createMarkdown ( outdatedPackages ) ;
169+ return {
170+ integrity : '1234567890' ,
171+ markdown,
172+ } ;
173+ }
174+
175+ // const result = {
176+ // integrity: '1234567890',
177+ // markdown: 'Hello from report.ts',
178+ // };
179+
180+ // console.log(JSON.stringify(result));
181+
182+ // main();
183+
184+ // TODO: try top level await.
185+
186+ // try {
187+
188+ // const stdout = execSync('yarn outdated --json', { encoding: 'utf-8', stdio: ['inherit', 'inherit', 'ignore'] });
189+ // console.log(stdout);
190+ // } catch (error) {
191+ // console.log(error);
192+ // }
193+
194+ // var s = spawnSync('yarn', ['outdated', '--json'], {
195+ // encoding: 'utf-8',
196+ // shell: true,
197+ // });
198+
199+ // console.log(s);
200+
201+ const report = main ( ) ;
202+ console . log ( JSON . stringify ( report ) ) ;
0 commit comments