@@ -7,8 +7,6 @@ import { parse as parseYaml } from 'yaml'
77import yarnLock from '@yarnpkg/lockfile'
88import JSON5 from 'json5'
99
10- export class UnsupportedLockfileError extends Error { }
11-
1210/**
1311 * The RepoData class is used to store and manipulate data about a repository, and serves as an abstraction
1412 * of the GitHub API.
@@ -31,7 +29,7 @@ export class RepoData {
3129 this . repoOwner = repoOwner
3230 this . repoName = repoName
3331 this . errorsThrown = [ ]
34- this . lockfileUnsupported = false
32+ this . rootLockfileVersion = null
3533 }
3634
3735 /**
@@ -295,22 +293,30 @@ export class RepoData {
295293 const results = [ ]
296294 // We want to try the root directory in all cases
297295 if ( ! packageObjects . some ( pkg => pkg . path === 'package.json' ) ) {
298- packageObjects . push ( { path : '' , content : { } } )
296+ packageObjects . push ( { path : 'package.json ' , content : { } } )
299297 }
300298 for ( const packageObject of packageObjects ) {
301299 try {
302300 const lockfileType = this . getLockfileType ( packageObject . path , tree )
303- const lockfilePath = packageObject . path . replace ( 'package.json' , lockfileType )
304- const lockfile = await this . getRepoFileContent ( packageObject . path . replace ( 'package.json' , lockfileType ) )
305- const lockfileObject = this . parseLockfile ( lockfile , lockfileType )
306- results . push ( await this . getIndirectDependencyFromLockfile ( lockfileObject , lockfileType , lockfilePath ) )
301+ if ( lockfileType ) {
302+ const lockfilePath = packageObject . path . replace ( 'package.json' , lockfileType )
303+ if ( this . checkFileExists ( lockfilePath , tree ) ) {
304+ const lockfile = await this . getRepoFileContent ( packageObject . path . replace ( 'package.json' , lockfileType ) )
305+ const lockfileObject = this . parseLockfile ( lockfile , lockfileType )
306+ const deps = await this . getIndirectDependencyFromLockfile ( lockfileObject , lockfileType , lockfilePath )
307+ if ( deps . length > 0 ) {
308+ results . push ( deps )
309+ }
310+ }
311+ }
307312 } catch ( error ) {
308313 this . handleError ( error )
309314 }
310315 }
311316 this . log ( `${ results . length } indirect dependencies found.` )
312-
313- return results
317+ if ( results . length > 0 ) {
318+ return results
319+ }
314320 }
315321
316322 /**
@@ -324,17 +330,28 @@ export class RepoData {
324330 this . log ( 'disambiguating direct dependencies' )
325331
326332 const results = [ ]
327- // We want to try the root directory in all cases
328- if ( ! dependencies . some ( dep => dep . packagePath === 'package.json' ) ) {
329- dependencies . push ( { packagePath : '' , specifiedVersion : '*' } )
333+ // We want to fall back to the root lockfile if we can't find a local lockfile
334+ if ( ! this . rootLockfileVersion ) {
335+ const rootLockfileType = this . getLockfileType ( 'package.json' , tree )
336+ if ( rootLockfileType ) {
337+ const rootLockfile = await this . getRepoFileContent ( rootLockfileType )
338+ const rootLockfileObject = this . parseLockfile ( rootLockfile , rootLockfileType )
339+ this . rootLockfileVersion = await this . getLockfileVersion ( rootLockfileObject , rootLockfileType , rootLockfileType , tree )
340+ }
330341 }
342+
331343 for ( const dependency of dependencies ) {
332344 try {
333345 if ( / ^ [ ~ ^ * ] / . test ( dependency . specifiedVersion ) ) {
334346 const lockfileType = this . getLockfileType ( dependency . packagePath , tree )
335- const lockfile = await this . getRepoFileContent ( dependency . packagePath . replace ( 'package.json' , lockfileType ) )
336- const lockfileObject = this . parseLockfile ( lockfile , lockfileType )
337- dependency . actualVersion = await this . getLockfileVersion ( lockfileObject , lockfileType , dependency . packagePath )
347+ const lockfilePath = dependency . packagePath . replace ( 'package.json' , lockfileType )
348+ if ( this . checkFileExists ( lockfilePath , tree ) ) {
349+ const lockfile = await this . getRepoFileContent ( lockfilePath )
350+ const lockfileObject = this . parseLockfile ( lockfile , lockfileType )
351+ dependency . actualVersion = await this . getLockfileVersion ( lockfileObject , lockfileType , dependency . packagePath , tree )
352+ } else {
353+ dependency . actualVersion = this . rootLockfileVersion ? this . rootLockfileVersion : dependency . specifiedVersion
354+ }
338355 } else {
339356 dependency . actualVersion = dependency . specifiedVersion
340357 }
@@ -357,12 +374,13 @@ export class RepoData {
357374 * @param {string } path - The path to the package.
358375 * @returns {Promise<String> } The lockfile govuk-frontend semver string.
359376 */
360- async getLockfileVersion ( lockfileObject , lockfileType , path ) {
377+ async getLockfileVersion ( lockfileObject , lockfileType , path , tree ) {
361378 let version
379+
362380 if ( lockfileType === 'package-lock.json' ) {
363381 version =
364- lockfileObject ?. packages ?. [ 'node_modules/govuk-frontend' ] ?. version ||
365- lockfileObject ?. dependencies ?. [ 'govuk-frontend' ] ?. version
382+ lockfileObject ?. packages ?. [ 'node_modules/govuk-frontend' ] ?. version ||
383+ lockfileObject ?. dependencies ?. [ 'govuk-frontend' ] ?. version
366384 } else if ( lockfileType === 'yarn.lock' ) {
367385 const dependencyKey = Object . keys ( lockfileObject ) . find ( key => key . startsWith ( 'govuk-frontend@' ) )
368386 version = lockfileObject [ dependencyKey ] ?. version
@@ -456,14 +474,14 @@ export class RepoData {
456474 *
457475 * @returns {string } - the lockfile type
458476 */
459- getLockfileType ( packagePath = '' , tree ) {
477+ getLockfileType ( packagePath = 'package.json ' , tree ) {
460478 let lockfileType
461- if ( this . checkFileExists ( 'package-lock.json' , tree ) || this . checkFileExists ( packagePath . replace ( 'package.json' , 'package-lock.json' ) , tree ) ) {
479+ if ( this . checkFileExists ( packagePath . replace ( 'package.json' , 'package-lock.json' ) , tree ) ) {
462480 lockfileType = 'package-lock.json'
463- } else if ( this . checkFileExists ( 'yarn.lock' , tree ) || this . checkFileExists ( packagePath . replace ( 'package.json' , 'yarn.lock' ) , tree ) ) {
481+ } else if ( this . checkFileExists ( packagePath . replace ( 'package.json' , 'yarn.lock' ) , tree ) ) {
464482 lockfileType = 'yarn.lock'
465483 } else {
466- throw new UnsupportedLockfileError ( )
484+ return null
467485 }
468486 return lockfileType
469487 }
@@ -476,9 +494,6 @@ export class RepoData {
476494 * @throws {Error } - If the error is not an expected type
477495 */
478496 handleError ( error ) {
479- if ( error instanceof UnsupportedLockfileError ) {
480- this . lockfileUnsupported = true
481- }
482497 this . log ( `${ error . message } . Added to result.` , 'error' )
483498 this . errorsThrown . push ( error . toString ( ) )
484499 }
0 commit comments