@@ -232,6 +232,155 @@ const getOwningComponentDirectory = checkPath => {
232
232
return null ;
233
233
} ;
234
234
235
+ /**
236
+ * Get the latest tag in a remote GitHub repository.
237
+ *
238
+ * @param {string } url The remote repository.
239
+ * @returns {Array }
240
+ */
241
+ const getRepositoryTags = async ( url ) => {
242
+ const gtr = require ( 'git-tags-remote' ) ;
243
+ try {
244
+ const tags = await gtr . get ( url ) ;
245
+ if ( tags !== undefined ) {
246
+ return tags ;
247
+ }
248
+ } catch ( error ) {
249
+ return [ ] ;
250
+ }
251
+ return [ ] ;
252
+ } ;
253
+
254
+ /**
255
+ * Get the list of thirdparty libraries that could be upgraded.
256
+ *
257
+ * @returns {Array }
258
+ */
259
+ const getThirdPartyLibsUpgradable = async ( ) => {
260
+ const libraries = getThirdPartyLibsData ( ) . filter ( ( library ) => ! ! library . repository ) ;
261
+ const upgradableLibraries = [ ] ;
262
+ const versionCompare = ( a , b ) => {
263
+ if ( a === b ) {
264
+ return 0 ;
265
+ }
266
+
267
+ const aParts = a . split ( '.' ) ;
268
+ const bParts = b . split ( '.' ) ;
269
+
270
+ for ( let i = 0 ; i < Math . min ( aParts . length , bParts . length ) ; i ++ ) {
271
+ const aPart = parseInt ( aParts [ i ] , 10 ) ;
272
+ const bPart = parseInt ( bParts [ i ] , 10 ) ;
273
+ if ( aPart > bPart ) {
274
+ // 1.1.0 > 1.0.9
275
+ return 1 ;
276
+ } else if ( aPart < bPart ) {
277
+ // 1.0.9 < 1.1.0
278
+ return - 1 ;
279
+ } else {
280
+ // Same version.
281
+ continue ;
282
+ }
283
+ }
284
+
285
+ if ( aParts . length > bParts . length ) {
286
+ // 1.0.1 > 1.0
287
+ return 1 ;
288
+ }
289
+
290
+ // 1.0 < 1.0.1
291
+ return - 1 ;
292
+ } ;
293
+
294
+ for ( let library of libraries ) {
295
+ upgradableLibraries . push (
296
+ getRepositoryTags ( library . repository ) . then ( ( tagMap ) => {
297
+ library . version = library . version . replace ( / ^ v / , '' ) ;
298
+ const currentVersion = library . version . replace ( / m o o d l e - / , '' ) ;
299
+ const currentMajorVersion = library . version . split ( '.' ) [ 0 ] ;
300
+ const tags = [ ...tagMap ]
301
+ . map ( ( tagData ) => tagData [ 0 ] )
302
+ . filter ( ( tag ) => ! tag . match ( / ( a l p h a | b e t a | r c ) / ) )
303
+ . map ( ( tag ) => tag . replace ( / ^ v / , '' ) )
304
+ . sort ( ( a , b ) => versionCompare ( b , a ) ) ;
305
+ if ( ! tags . length ) {
306
+ library . warning = "Unable to find any comparable tags." ;
307
+ return library ;
308
+ }
309
+
310
+ library . latestVersion = tags [ 0 ] ;
311
+ tags . some ( ( tag ) => {
312
+ if ( ! tag ) {
313
+ return false ;
314
+ }
315
+
316
+ // See if the version part matches.
317
+ const majorVersion = tag . split ( '.' ) [ 0 ] ;
318
+ if ( majorVersion === currentMajorVersion ) {
319
+ library . latestSameMajorVersion = tag ;
320
+ return true ;
321
+ }
322
+ return false ;
323
+ } ) ;
324
+
325
+
326
+ if ( versionCompare ( currentVersion , library . latestVersion ) > 0 ) {
327
+ // Moodle somehow has a newer version than the latest version.
328
+ library . warning = `Newer version found: ${ currentVersion } > ${ library . latestVersion } for ${ library . name } ` ;
329
+ return library ;
330
+ }
331
+
332
+
333
+ if ( library . version !== library . latestVersion ) {
334
+ // Delete version and add it again at the end of the array. That way, current and new will stay closer.
335
+ delete library . version ;
336
+ library . version = currentVersion ;
337
+ return library ;
338
+ }
339
+ return null ;
340
+ } )
341
+ ) ;
342
+ }
343
+
344
+ return ( await Promise . all ( upgradableLibraries ) ) . filter ( ( library ) => ! ! library ) ;
345
+ } ;
346
+
347
+ /**
348
+ * Get the list of thirdparty libraries.
349
+ *
350
+ * @returns {Array }
351
+ */
352
+ const getThirdPartyLibsData = ( ) => {
353
+ const DOMParser = require ( 'xmldom' ) . DOMParser ;
354
+ const fs = require ( 'fs' ) ;
355
+ const xpath = require ( 'xpath' ) ;
356
+ const path = require ( 'path' ) ;
357
+
358
+ const libraryList = [ ] ;
359
+ const libraryFields = [
360
+ 'location' ,
361
+ 'name' ,
362
+ 'version' ,
363
+ 'repository' ,
364
+ ] ;
365
+
366
+ const thirdpartyfiles = getThirdPartyLibsList ( fs . realpathSync ( './' ) ) ;
367
+ thirdpartyfiles . forEach ( function ( libraryPath ) {
368
+ const xmlContent = fs . readFileSync ( libraryPath , 'utf8' ) ;
369
+ const doc = new DOMParser ( ) . parseFromString ( xmlContent ) ;
370
+ const libraries = xpath . select ( "/libraries/library" , doc ) ;
371
+ for ( const library of libraries ) {
372
+ const libraryData = [ ] ;
373
+ for ( const field of libraryFields ) {
374
+ libraryData [ field ] = xpath . select ( `${ field } /text()` , library ) ?. toString ( ) ;
375
+ }
376
+ libraryData . location = path . join ( path . dirname ( libraryPath ) , libraryData . location ) ;
377
+ libraryList . push ( libraryData ) ;
378
+ }
379
+ } ) ;
380
+
381
+ return libraryList . sort ( ( a , b ) => a . location . localeCompare ( b . location ) ) ;
382
+ } ;
383
+
235
384
module . exports = {
236
385
fetchComponentData,
237
386
getAmdSrcGlobList,
@@ -241,4 +390,5 @@ module.exports = {
241
390
getYuiSrcGlobList,
242
391
getThirdPartyLibsList,
243
392
getThirdPartyPaths,
393
+ getThirdPartyLibsUpgradable,
244
394
} ;
0 commit comments