import chalk from 'chalk' ; import crypto from 'crypto' ; import findCacheDir from 'find-cache-dir' ; import { findUpSync , pathExistsSync } from 'find-up' ; import fse , { SymlinkType } from 'fs-extra' ; import { globbySync as glob , Options as GlobbyOptions } from 'globby' ; import yaml from 'js-yaml' ; import { reduce } from 'lodash-es' ; import type { StdioOptions } from 'node:child_process' ; import { createRequire } from 'node:module' ; import { dirname , join , resolve } from 'node:path' ; import { fileURLToPath } from 'node:url' ; import { packageDirectorySync } from 'pkg-dir' ; import { TFlags , TFlagsMapping } from './ifaces' ; const cp = createRequire ( import . meta . url ) ( 'child_process' ) ; const { ensureDirSync , readFileSync } = fse ; const __dirname = dirname ( fileURLToPath ( import . meta . url ) ) ; export const pkgDir = ( cwd ) : string => packageDirectorySync ( { cwd } ) ; export const invoke = ( cmd , args , cwd , silent = false , inherit = true , skipError = false ) : string | ReturnType < typeof cp . spawnSync > => { ! silent && console . log ( chalk . bold ( 'invoke' ) , cmd , ... args ) ; const stdio = inherit ? [ 'inherit' , 'inherit' , 'inherit' ] : [ null , null , null ] ; const result = cp . spawnSync ( cmd , args , { cwd , stdio } ) ; if ( ! skipError && ( result . error || result . status ) ) { throw result ; } return String ( result . stdout ?. toString ( ) . trim ( ) ) ; } ; const checkValue = ( key , value , omitlist , picklist ) : boolean => value !== 'false' && ! omitlist . includes ( key ) && ( picklist . length === 0 || picklist . includes ( key ) ) ; const formatFlag = ( key ) : string => ( key . length === 1 ? '-' : '--' ) + key ; const camelToKebab = ( string ) : string => string . replace ( /([\da-z]|(?=[A-Z]))([A-Z])/g , '$1-$2' ) . toLowerCase ( ) ; export const normalizeFlags = ( flags ) : TFlags => Object . keys ( flags ) . reduce < TFlags > ( ( m , key ) => { m [ camelToKebab ( key ) ] = flags [ key ] ; return m ; } , { } ) ; export const formatFlags = ( flags , ... picklist ) : string [ ] => Object . keys ( flags ) . reduce < string [ ] > ( ( memo , key ) => { const omitlist = [ '_' , '--' ] ; const value = flags [ key ] ; const flag = formatFlag ( key ) ; if ( checkValue ( key , value , omitlist , picklist ) ) { memo . push ( flag ) ; if ( value !== true ) { memo . push ( value ) ; } } return memo ; } , [ ] ) ; export const mapFlags = ( flags , mapping ) : TFlags => reduce ( flags , ( memo , value , key ) => { const repl = mapping [ key ] ; let k = key ; let v = value ; if ( repl ) { if ( typeof repl === 'string' ) { k = repl ; } else { k = repl ?. key ?? k ; v = repl ?. value ?? repl ?. values ?. [ value ] ?? v ; } } memo [ k ] = v ; return memo ; } , { } ) ; export const isWindows = ( ) : boolean => process . platform === 'win32' || /^(msys|cygwin)$/ . test ( process . env . OSTYPE as string ) ; export const getSymlinkType = ( type ? ) : SymlinkType => ( type as SymlinkType ) || ( isWindows ( ) ? 'junction' : 'dir' ) ; export const getYarn = ( ) : string => ( isWindows ( ) ? 'yarn.cmd' : 'yarn' ) ; export const getClosestBin = ( cmd ) : string => String ( findUpSync ( ( dir ) => { const ref = resolve ( dir , 'node_modules' , '.bin' , cmd ) ; return pathExistsSync ( ref ) ? ref : undefined ; } , { cwd : String ( pkgDir ( __dirname ) ) , } ) ) ; export const getNpm = ( npmPath = 'system' , isWin = isWindows ( ) ) : string => { const cmd = isWin ? 'npm.cmd' : 'npm' ; if ( npmPath === 'system' ) { return cmd ; } if ( npmPath === 'local' ) { return getClosestBin ( cmd ) ; } return npmPath ; } ; export const getWorkspaces = ( cwd , manifest ) : string [ ] => { let packages = manifest . workspaces ; if ( packages && packages . packages ) { packages = packages . packages ; } if ( ! packages || packages . length === 0 ) { return [ ] ; } return glob ( packages . map ( ( p ) => p . replace ( /\/?$/ , '/package.json' ) ) , { cwd , onlyFiles : true , absolute : true , gitignore : true , } as GlobbyOptions ) ; } ; export const readJson = ( path ) : any => JSON . parse ( readFileSync ( path ) . toString ( 'utf-8' ) . trim ( ) ) ; export const ensureDir = ( dir ) : string => { ensureDirSync ( dir ) ; return dir ; } ; export const getTemp = ( cwd , temp ? ) : string => { if ( temp ) { return ensureDir ( resolve ( temp ) ) ; } const id = crypto . randomBytes ( 16 ) . toString ( 'hex' ) ; const cacheDir = findCacheDir ( { name : 'yarn-audit-fix' , cwd } ) + '' ; const tempDir = join ( cacheDir , id ) ; return ensureDir ( tempDir ) ; } ; export const attempt = < T > ( f ) : T | null => { try { return f ( ) ; } catch { return null ; } } ; export const parseYaml = < T = Record < string , any > > ( contents ) : T => { try { return yaml . load ( contents ) as T ; } catch ( e ) { throw new Error ( `YAML required: ${ e }` ) ; } } ; export const formatYaml = yaml . dump ; export const getBinVersion = ( bin , cwd = process . cwd ( ) ) : string => invoke ( bin , [ '--version' ] , cwd , true , false ) ;