import { relative , resolve } from 'path' ; import { readFileSync } from 'fs' ; import { promisify } from 'util' ; import licenseChecker from 'license-checker' ; export type InstalledPackage = NonNullable < ReturnType < typeof readModuleInfo > > ; interface Options { directory : string ; includeDev ? : boolean ; licenseOverrides ? : { [ pgkNameAndVersion ] : string [ ] ; } ; } const toArray = < T > ( input ) => ( [ ] as T [ ] ) . concat ( input ) ; function resolveLicenses ( isPrivate , realPath , licenses ) { if ( isPrivate && licenses === 'UNLICENSED' ) { try { const pkg = JSON . parse ( readFileSync ( resolve ( realPath , 'package.json' ) , 'utf8' ) ) ; if ( ! pkg . license ) { throw new Error ( 'no license field' ) ; } return [ pkg . license as string ] ; } catch ( error ) { throw new Error ( `Unable to detect license for \`"private": true\` package at ${ realPath }: ${ error . message }` ) ; } } return toArray ( licenses || [ ] ) ; } function readModuleInfo ( pkgAndVersion , moduleInfo , dev , options ) { const directory = ( moduleInfo as any ) . realPath as string ; if ( directory === options . directory ) { return ; } const isPrivate = ! ! ( moduleInfo as any ) . private as boolean ; const keyParts = pkgAndVersion . split ( '@' ) ; const name = keyParts . slice ( 0 , - 1 ) . join ( '@' ) ; const version = keyParts [ keyParts . length - 1 ] ; const override = options . licenseOverrides && options . licenseOverrides [ pkgAndVersion ] ; return { name , version , isDevOnly : dev , repository : moduleInfo . repository , directory , relative : relative ( options . directory , directory ) , licenses : toArray ( override ? override : resolveLicenses ( isPrivate , directory , moduleInfo . licenses ) ) , } ; } async function _getInstalledPackages ( dev , options ) { const lcResult = await promisify ( licenseChecker . init ) ( { start : options . directory , development : dev , production : ! dev , json : true , customFormat : { realPath : true , licenseText : false , licenseFile : false , } , } as any ) ; const result = [ ] ; for ( const [ pkgAndVersion , moduleInfo ] of Object . entries ( lcResult ) ) { const installedPackage = readModuleInfo ( pkgAndVersion , moduleInfo , dev , options ) ; if ( installedPackage ) { result . push ( installedPackage ) ; } } return result ; } /**
 *  Get a list of objects with details about each installed
 *  NPM package.
 */ export async function getInstalledPackages ( options ) { return [ ... ( await _getInstalledPackages ( false , options ) ) , ... ( options . includeDev ? await _getInstalledPackages ( true , options ) : [ ] ) , ] ; }