import { platform } from 'os' ; import { existsSync , readFileSync , mkdirSync , writeFileSync } from 'fs' ; import { normalize , join } from 'path' ; import { ExtensionContext } from 'vscode' ; import { PluginResourceSettings , hasUserSetPathToJest } from './Settings' ; import { TestIdentifier } from './TestResults' ; import { TestStats } from './types' ; /**
 * Known binary names of `react-scripts` forks
 */ const createReactAppBinaryNames = [ 'react-scripts' , 'react-native-scripts' , 'react-scripts-ts' , 'react-app-rewired' , ] ; /**
 * File extension for npm binaries
 */ export const nodeBinExtension = platform ( ) === 'win32' ? '.cmd' : '' ; /**
 * Resolves the location of an npm binary
 *
 * Returns the path if it exists, or `undefined` otherwise
 */ function getLocalPathForExecutable ( rootPath , executable ) { const absolutePath = normalize ( join ( rootPath , 'node_modules' , '.bin' , executable + nodeBinExtension ) ) ; return existsSync ( absolutePath ) ? absolutePath : undefined ; } /**
 * Tries to read the test command from the scripts section within `package.json`
 *
 * Returns the test command in case of success,
 * `undefined` if there was an exception while reading and parsing `package.json`
 * `null` if there is no test script
 */ export function getTestCommand ( rootPath ) { try { const packagePath = join ( rootPath , 'package.json' ) ; const packageJSON = JSON . parse ( readFileSync ( packagePath , 'utf8' ) ) ; if ( packageJSON && packageJSON . scripts && packageJSON . scripts . test ) { return packageJSON . scripts . test ; } return null ; } catch { return undefined ; } } /**
 * Checks if the supplied test command could have been generated by create-react-app
 */ export function isCreateReactAppTestCommand ( testCommand ? ) { return ( ! ! testCommand && createReactAppBinaryNames . some ( ( binary ) => testCommand . includes ( `${ binary } test` ) ) ) ; } /**
 * Checks if the project in `rootPath` was bootstrapped by `create-react-app`.
 */ function isBootstrappedWithCreateReactApp ( rootPath ) { const testCommand = getTestCommand ( rootPath ) ; if ( testCommand === undefined ) { return createReactAppBinaryNames . some ( ( binary ) => getLocalPathForExecutable ( rootPath , binary ) !== undefined ) ; } return isCreateReactAppTestCommand ( testCommand ) ; } returns { string } export function pathToJest ( { pathToJest , rootPath } ) { if ( pathToJest && hasUserSetPathToJest ( pathToJest ) ) { return normalize ( pathToJest ) ; } if ( isBootstrappedWithCreateReactApp ( rootPath ) ) { return 'npm test --' ; } const p = getLocalPathForExecutable ( rootPath , 'jest' ) || 'jest' + nodeBinExtension ; return `"${ p }"` ; } returns { string } export function pathToConfig ( pluginSettings ) { if ( pluginSettings . pathToConfig ) { return normalize ( pluginSettings . pathToConfig ) ; } return '' ; } /**
 *  Taken From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
 */ export function escapeRegExp ( str ) { return str . replace ( /[.*+?^${}()|[\]\\]/g , '\\$&' ) ; } /**
 * ANSI colors/characters cleaning based on http://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
 */ export function cleanAnsi ( str ) { return str . replace ( /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g , '' ) ; } export type IdStringType = 'display' | 'display-reverse' | 'full-name' ; export function testIdString ( type , identifier ) { if ( ! identifier . ancestorTitles . length ) { return identifier . title ; } const parts = [ ... identifier . ancestorTitles , identifier . title ] ; switch ( type ) { case 'display' : return parts . join ( ' > ' ) ; case 'display-reverse' : return parts . reverse ( ) . join ( ' < ' ) ; case 'full-name' : return parts . join ( ' ' ) ; } } /** convert the upper-case drive letter filePath to lower-case. If path does not contain upper-case drive letter, returns undefined. */ export function toLowerCaseDriveLetter ( filePath ) { const match = filePath . match ( /^([A-Z]:\\)(.*)$/ ) ; if ( match ) { return `${ match [ 1 ] . toLowerCase ( ) }${ match [ 2 ] }` ; } } /** convert the lower-case drive letter filePath (like vscode.URI.fsPath) to lower-case. If path does not contain lower-case drive letter, returns undefined. */ export function toUpperCaseDriveLetter ( filePath ) { const match = filePath . match ( /^([a-z]:\\)(.*)$/ ) ; if ( match ) { return `${ match [ 1 ] . toUpperCase ( ) }${ match [ 2 ] }` ; } } param filePath export function toFilePath ( filePath ) { return toUpperCaseDriveLetter ( filePath ) || filePath ; } /**
 * Generate path to icon used in decorations
 * NOTE: Should not be called repeatedly for the performance reasons. Cache your results.
 */ export function prepareIconFile ( context , iconName , source , color ? ) { const iconsPath = join ( 'generated-icons' ) ; const resolvePath = ( ... args ) : string => { return context . asAbsolutePath ( join ( ... args ) ) ; } ; const resultIconPath = resolvePath ( iconsPath , `${ iconName }.svg` ) ; let result = source . toString ( ) ; if ( color ) { result = result . replace ( 'fill="currentColor"' , `fill="${ color }"` ) ; } if ( ! existsSync ( resultIconPath ) || readFileSync ( resultIconPath ) . toString ( ) !== result ) { if ( ! existsSync ( resolvePath ( iconsPath ) ) ) { mkdirSync ( resolvePath ( iconsPath ) ) ; } writeFileSync ( resultIconPath , result ) ; } return resultIconPath ; } const SurroundingQuoteRegex = /^["']|["']$/g ; export const removeSurroundingQuote = ( command ) : string => command . replace ( SurroundingQuoteRegex , '' ) ; export const emptyTestStats = ( ) : TestStats => { return { success : 0 , fail : 0 , unknown : 0 } ; } ; /**
 * quoting a given string for it to be used as shell command arguments.
 *
 * Note: the logic is based on vscode's debug argument handling:
 * https://github.com/microsoft/vscode/blob/c0001d7becf437944f5898a7c9485922d60dd8d3/src/vs/workbench/contrib/debug/node/terminals.ts#L82 .
 * However, had to modify a few places for windows platform.
 *
 **/ export const shellQuote = ( str , shell ? ) : string => { const targetShell = shell && shell . trim ( ) . toLowerCase ( ) ; let shellType ; if ( ! targetShell ) { shellType = platform ( ) === 'win32' ? 'cmd' : 'sh' ; } else if ( targetShell . indexOf ( 'powershell' ) >= 0 || targetShell . indexOf ( 'pwsh' ) >= 0 ) { shellType = 'powershell' ; } else if ( targetShell . indexOf ( 'cmd.exe' ) >= 0 ) { shellType = 'cmd' ; } else { shellType = 'sh' ; } switch ( shellType ) { case 'powershell' : { const s = str . replace ( /(['"])/g , '$1$1' ) ; if ( s . length > 2 && s . slice ( - 2 ) === '\\\\' ) { return `'${ s }\\\\'` ; } return `'${ s }'` ; } case 'cmd' : { let s = str . replace ( /"/g , '""' ) ; if ( s . length > 2 && s . slice ( - 2 ) === '\\\\' ) { s = `${ s }\\\\` ; } return s . indexOf ( ' ' ) >= 0 || s . indexOf ( '"' ) >= 0 || s . length === 0 ? `"${ s }"` : s ; } default : { const s = str . replace ( /(["'\\$])/g , '\\$1' ) ; return s . indexOf ( ' ' ) >= 0 || s . indexOf ( ';' ) >= 0 || s . length === 0 ? `"${ s }"` : s ; } } } ;