@@ -274,6 +274,71 @@ function toBase64(hexString) {
274
274
return Buffer . from ( hexString , "hex" ) . toString ( "base64" ) ;
275
275
}
276
276
277
+ /**
278
+ * Return the current timestamp in YYYY-MM-DDTHH:MM:SSZ format.
279
+ *
280
+ * @returns {string } ISO formatted timestamp, without milliseconds.
281
+ */
282
+ export function getTimestamp ( ) {
283
+ return new Date ( ) . toISOString ( ) . split ( "." ) [ 0 ] + "Z" ;
284
+ }
285
+
286
+ /**
287
+ * Method to determine if a license is a valid SPDX license expression
288
+ *
289
+ * @param {string } license License string
290
+ * @returns {boolean } true if the license is a valid SPDX license expression
291
+ * @see https://spdx.dev/learn/handling-license-info/
292
+ **/
293
+ export function isSpdxLicenseExpression ( license ) {
294
+ if ( ! license ) {
295
+ return false ;
296
+ }
297
+
298
+ if ( / [ ( \s ] + / g. test ( license ) ) {
299
+ return true ;
300
+ }
301
+
302
+ if ( license . endsWith ( "+" ) ) {
303
+ return true ; // GPL-2.0+ means GPL-2.0 or any later version, at the licensee’s option.
304
+ }
305
+
306
+ return false ;
307
+ }
308
+
309
+ /**
310
+ * Convert the array of licenses to a CycloneDX 1.5 compliant license array.
311
+ * This should return an array containing:
312
+ * - one or more SPDX license if no expression is present
313
+ * - the first license expression if at least one is present
314
+ *
315
+ * @param {Array } licenses Array of licenses
316
+ * @returns {Array } CycloneDX 1.5 compliant license array
317
+ */
318
+ export function adjustLicenseInformation ( licenses ) {
319
+ if ( ! licenses || ! Array . isArray ( licenses ) ) {
320
+ return [ ] ;
321
+ }
322
+
323
+ const expressions = licenses . filter ( ( f ) => {
324
+ return f . expression ;
325
+ } ) ;
326
+ if ( expressions . length >= 1 ) {
327
+ if ( expressions . length > 1 ) {
328
+ console . warn ( "multiple license expressions found" , expressions ) ;
329
+ }
330
+ return [ { expression : expressions [ 0 ] . expression } ] ;
331
+ } else {
332
+ return licenses . map ( ( l ) => {
333
+ if ( typeof l . license === "object" ) {
334
+ return l ;
335
+ } else {
336
+ return { license : l } ;
337
+ }
338
+ } ) ;
339
+ }
340
+ }
341
+
277
342
/**
278
343
* Performs a lookup + validation of the license specified in the
279
344
* package. If the license is a valid SPDX license ID, set the 'id'
@@ -286,8 +351,8 @@ export function getLicenses(pkg) {
286
351
if ( ! Array . isArray ( license ) ) {
287
352
license = [ license ] ;
288
353
}
289
- return license
290
- . map ( ( l ) => {
354
+ return adjustLicenseInformation (
355
+ license . map ( ( l ) => {
291
356
let licenseContent = { } ;
292
357
if ( typeof l === "string" || l instanceof String ) {
293
358
if (
@@ -309,6 +374,8 @@ export function getLicenses(pkg) {
309
374
licenseContent . name = "CUSTOM" ;
310
375
}
311
376
licenseContent . url = l ;
377
+ } else if ( isSpdxLicenseExpression ( l ) ) {
378
+ licenseContent . expression = l ;
312
379
} else {
313
380
licenseContent . name = l ;
314
381
}
@@ -322,7 +389,7 @@ export function getLicenses(pkg) {
322
389
}
323
390
return licenseContent ;
324
391
} )
325
- . map ( ( l ) => ( { license : l } ) ) ;
392
+ ) ;
326
393
} else {
327
394
const knownLicense = getKnownLicense ( undefined , pkg ) ;
328
395
if ( knownLicense ) {
0 commit comments