import { runtime } from "../runtimes" ; import { ArrayUtils , StringUtils } from "../utils" ; import { FileSystemHost } from "./FileSystemHost" ; import { StandardizedFilePath } from "./StandardizedFilePath" ; import { TransactionalFileSystem } from "./TransactionalFileSystem" ; const isWindowsRootDirRegex = /^[a-z]+:[\\\/]$/i ; const path = runtime . path ; /** Utilities for working with files. */ export class FileUtils { internal private static standardizeSlashesRegex = /\\/g ; internal private static trimSlashStartRegex = /^\// ; internal private static trimSlashEndRegex = /\/$/ ; static readonly ENOENT = "ENOENT" ; private constructor ( ) { } param err static isNotExistsError ( err ) { return err != null && err . code === FileUtils . ENOENT || err != null && err ?. constructor ?. name === "NotFound" ; } param paths static pathJoin < T extends string > ( basePath , ... paths ) { if ( FileUtils . pathIsAbsolute ( basePath ) ) { return FileUtils . standardizeSlashes ( path . normalize ( path . join ( basePath , ... paths ) ) ) as T ; } return FileUtils . standardizeSlashes ( path . join ( basePath , ... paths ) ) as T ; } param fileOrDirPath static pathIsAbsolute ( fileOrDirPath ) { return isAbsolutePath ( fileOrDirPath ) ; } param fileSystem param fileOrDirPath param relativeBase static getStandardizedAbsolutePath ( fileSystem , fileOrDirPath , relativeBase ? ) { return FileUtils . standardizeSlashes ( path . normalize ( getAbsolutePath ( ) ) ) as StandardizedFilePath ; function getAbsolutePath ( ) { if ( isAbsolutePath ( fileOrDirPath ) ) return fileOrDirPath ; if ( ! fileOrDirPath . startsWith ( "./" ) && relativeBase != null ) return path . join ( relativeBase , fileOrDirPath ) ; return path . join ( fileSystem . getCurrentDirectory ( ) , fileOrDirPath ) ; } } param fileOrDirPath static getDirPath < T extends string > ( fileOrDirPath ) { fileOrDirPath = FileUtils . standardizeSlashes ( fileOrDirPath ) ; const lastIndexOfSlash = fileOrDirPath . lastIndexOf ( "/" ) ; if ( lastIndexOfSlash === - 1 ) return "." as T ; return FileUtils . standardizeSlashes ( fileOrDirPath . substring ( 0 , lastIndexOfSlash + 1 ) as T ) ; } param fileOrDirPath static getBaseName ( fileOrDirPath ) { const lastIndexOfSlash = fileOrDirPath . lastIndexOf ( "/" ) ; return fileOrDirPath . substring ( lastIndexOfSlash + 1 ) ; } param fileOrDirPath static getExtension ( fileOrDirPath ) { const baseName = FileUtils . getBaseName ( fileOrDirPath ) ; const lastDotIndex = baseName . lastIndexOf ( "." ) ; if ( lastDotIndex <= 0 ) return "" ; const lastExt = baseName . substring ( lastDotIndex ) ; const lastExtLowerCase = lastExt . toLowerCase ( ) ; if ( lastExtLowerCase === ".ts" && baseName . substring ( lastDotIndex - 2 , lastDotIndex ) . toLowerCase ( ) === ".d" ) return baseName . substring ( lastDotIndex - 2 ) ; if ( lastExtLowerCase === ".map" && baseName . substring ( lastDotIndex - 3 , lastDotIndex ) . toLowerCase ( ) === ".js" ) return baseName . substring ( lastDotIndex - 3 ) ; return lastExt ; } param fileOrDirPath static standardizeSlashes < T extends string > ( fileOrDirPath ) { let result = fileOrDirPath . replace ( this . standardizeSlashesRegex , "/" ) ; if ( ! FileUtils . isRootDirPath ( result ) && result . endsWith ( "/" ) ) result = result . substring ( 0 , result . length - 1 ) ; return result as T ; } param fileOrDirPath param endsWithPath static pathEndsWith ( fileOrDirPath , endsWithPath ) { const pathItems = FileUtils . splitPathBySlashes ( fileOrDirPath ) ; const endsWithItems = FileUtils . splitPathBySlashes ( endsWithPath ) ; if ( endsWithItems . length > pathItems . length ) return false ; for ( let i = 0 ; i < endsWithItems . length ; i ++ ) { if ( endsWithItems [ endsWithItems . length - i - 1 ] !== pathItems [ pathItems . length - i - 1 ] ) return false ; } return endsWithItems . length > 0 ; } param fileOrDirPath param startsWithPath static pathStartsWith ( fileOrDirPath , startsWithPath ) { const isfileOrDirPathEmpty = StringUtils . isNullOrWhitespace ( fileOrDirPath ) ; const isStartsWithPathEmpty = StringUtils . isNullOrWhitespace ( startsWithPath ) ; const pathItems = FileUtils . splitPathBySlashes ( fileOrDirPath ) ; const startsWithItems = FileUtils . splitPathBySlashes ( startsWithPath ) ; if ( isfileOrDirPathEmpty && isStartsWithPathEmpty ) return true ; if ( isStartsWithPathEmpty || startsWithItems . length > pathItems . length ) return false ; if ( startsWithItems . length === 1 && startsWithItems [ 0 ] . length === 0 ) return true ; for ( let i = 0 ; i < startsWithItems . length ; i ++ ) { if ( startsWithItems [ i ] !== pathItems [ i ] ) return false ; } return startsWithItems . length > 0 ; } private static splitPathBySlashes ( fileOrDirPath ) { fileOrDirPath = ( fileOrDirPath || "" ) . replace ( FileUtils . trimSlashStartRegex , "" ) . replace ( FileUtils . trimSlashEndRegex , "" ) ; return FileUtils . standardizeSlashes ( fileOrDirPath ) . replace ( /^\// , "" ) . split ( "/" ) ; } param paths static getParentMostPaths ( paths ) { const finalPaths = [ ] ; for ( const fileOrDirPath of ArrayUtils . sortByProperty ( paths , p => p . length ) ) { if ( finalPaths . every ( p => ! FileUtils . pathStartsWith ( fileOrDirPath , p ) ) ) finalPaths . push ( fileOrDirPath ) ; } return finalPaths ; } param fileSystem param filePath param encoding static async readFileOrNotExists ( fileSystem , filePath , encoding ) { try { return await fileSystem . readFile ( filePath , encoding ) ; } catch ( err ) { if ( ! FileUtils . isNotExistsError ( err ) ) throw err ; return false ; } } param fileSystem param filePath param encoding static readFileOrNotExistsSync ( fileSystem , filePath , encoding ) { try { return fileSystem . readFileSync ( filePath , encoding ) ; } catch ( err ) { if ( ! FileUtils . isNotExistsError ( err ) ) throw err ; return false ; } } param text static getTextWithByteOrderMark ( text ) { if ( StringUtils . hasBom ( text ) ) return text ; return "\uFEFF" + text ; } param absoluteDirPathFrom param absolutePathTo static getRelativePathTo ( absoluteDirPathFrom , absolutePathTo ) { const relativePath = path . relative ( absoluteDirPathFrom , FileUtils . getDirPath ( absolutePathTo ) ) ; return FileUtils . standardizeSlashes ( path . join ( relativePath , FileUtils . getBaseName ( absolutePathTo ) ) ) as StandardizedFilePath ; } param path static isRootDirPath ( dirOrFilePath ) { return dirOrFilePath === "/" || isWindowsRootDirRegex . test ( dirOrFilePath ) ; } param dirPath static * getDescendantDirectories ( fileSystemWrapper , dirPath ) { for ( const entry of fileSystemWrapper . readDirSync ( dirPath ) ) { if ( ! entry . isDirectory ) continue ; yield entry . path ; yield * FileUtils . getDescendantDirectories ( fileSystemWrapper , entry . path ) ; } } param glob param cwd static toAbsoluteGlob ( glob , cwd ) { if ( glob . slice ( 0 , 2 ) === "./" ) glob = glob . slice ( 2 ) ; if ( glob . length === 1 && glob === "." ) glob = "" ; const suffix = glob . slice ( - 1 ) ; const isNegated = FileUtils . isNegatedGlob ( glob ) ; if ( isNegated ) glob = glob . slice ( 1 ) ; if ( ! isAbsolutePath ( glob ) || glob . slice ( 0 , 1 ) === "\\" ) glob = globJoin ( cwd , glob ) ; if ( suffix === "/" && glob . slice ( - 1 ) !== "/" ) glob += "/" ; return isNegated ? "!" + glob : glob ; } param glob static isNegatedGlob ( glob ) { return glob [ 0 ] === "!" && glob [ 1 ] !== "(" ; } } function globJoin ( dir , glob ) { if ( dir . charAt ( dir . length - 1 ) === "/" ) dir = dir . slice ( 0 , - 1 ) ; if ( glob . charAt ( 0 ) === "/" ) glob = glob . slice ( 1 ) ; if ( ! glob ) return dir ; return dir + "/" + glob ; } function isAbsolutePath ( filePath ) { return filePath . startsWith ( "/" ) || isWindowsAbsolutePath ( filePath ) ; } const isWindowsAbsolutePathRegex = /^[a-z]+:[\\\/]/i ; function isWindowsAbsolutePath ( filePath ) { return isWindowsAbsolutePathRegex . test ( filePath ) || isAzureAbsolutePath ( filePath ) || isUncPath ( filePath ) ; } function isAzureAbsolutePath ( filePath ) { return filePath . startsWith ( "\\\\" ) ; } const uncPathRegex = /^[\\\/]{2,}[^\\\/]+[\\\/]+[^\\\/]+/ ; function isUncPath ( filePath ) { return uncPathRegex . test ( filePath ) ; }