namespace vfs { /**
     * Posix-style path to the TypeScript compiler build outputs (including tsc.js, lib.d.ts, etc.)
     */ export const builtFolder = "/.ts" ; /**
     * Posix-style path to additional mountable folders (./tests/projects in this repo)
     */ export const projectsFolder = "/.projects" ; /**
     * Posix-style path to additional test libraries
     */ export const testLibFolder = "/.lib" ; /**
     * Posix-style path to sources under test
     */ export const srcFolder = "/.src" ; const S_IFMT = 0o170000 ; const S_IFSOCK = 0o140000 ; const S_IFLNK = 0o120000 ; const S_IFREG = 0o100000 ; const S_IFBLK = 0o060000 ; const S_IFDIR = 0o040000 ; const S_IFCHR = 0o020000 ; const S_IFIFO = 0o010000 ; let devCount = 0 ; let inoCount = 0 ; export interface DiffOptions { includeChangedFileWithSameContent ? : boolean ; baseIsNotShadowRoot ? : boolean ; } /**
     * Represents a virtual POSIX-like file system.
     */ export class FileSystem { /** Indicates whether the file system is case-sensitive (`false`) or case-insensitive (`true`). */ public readonly ignoreCase : boolean ; /** Gets the comparison function used to compare two paths. */ public readonly stringComparer : ( a : string , b : string ) => number ; private _lazy : { links ? : collections . SortedMap < string , Inode > ; shadows ? : Map < number , Inode > ; meta ? : collections . Metadata ; } = { } ; private _cwd : string ; private _time : number | Date | ( ( ) => number | Date ) ; private _shadowRoot : FileSystem | undefined ; private _dirStack : string [ ] | undefined ; constructor ( ignoreCase , options = { } ) { const { time = - 1 , files , meta } = options ; this . ignoreCase = ignoreCase ; this . stringComparer = this . ignoreCase ? vpath . compareCaseInsensitive : vpath . compareCaseSensitive ; this . _time = time ; if ( meta ) { for ( const key of Object . keys ( meta ) ) { this . meta . set ( key , meta [ key ] ) ; } } if ( files ) { this . _applyFiles ( files , "" ) ; } let cwd = options . cwd ; if ( ( ! cwd || ! vpath . isRoot ( cwd ) ) && this . _lazy . links ) { const iterator = collections . getIterator ( this . _lazy . links . keys ( ) ) ; try { for ( let i = collections . nextResult ( iterator ) ; i ; i = collections . nextResult ( iterator ) ) { const name = i . value ; cwd = cwd ? vpath . resolve ( name , cwd ) : name ; break ; } } finally { collections . closeIterator ( iterator ) ; } } if ( cwd ) { vpath . validate ( cwd , vpath . ValidationFlags . Absolute ) ; this . mkdirpSync ( cwd ) ; } this . _cwd = cwd || "" ; } /**
         * Gets metadata for this `FileSystem`.
         */ public get meta ( ) : collections . Metadata { if ( ! this . _lazy . meta ) { this . _lazy . meta = new collections . Metadata ( this . _shadowRoot ? this . _shadowRoot . meta : undefined ) ; } return this . _lazy . meta ; } /**
         * Gets a value indicating whether the file system is read-only.
         */ public get isReadonly ( ) { return Object . isFrozen ( this ) ; } /**
         * Makes the file system read-only.
         */ public makeReadonly ( ) { Object . freeze ( this ) ; return this ; } /**
         * Gets the file system shadowed by this file system.
         */ public get shadowRoot ( ) { return this . _shadowRoot ; } /**
         * Snapshots the current file system, effectively shadowing itself. This is useful for
         * generating file system patches using `.diff()` from one snapshot to the next. Performs
         * no action if this file system is read-only.
         */ public snapshot ( ) { if ( this . isReadonly ) return ; const fs = new FileSystem ( this . ignoreCase , { time : this . _time } ) ; fs . _lazy = this . _lazy ; fs . _cwd = this . _cwd ; fs . _time = this . _time ; fs . _shadowRoot = this . _shadowRoot ; fs . _dirStack = this . _dirStack ; fs . makeReadonly ( ) ; this . _lazy = { } ; this . _shadowRoot = fs ; } /**
         * Gets a shadow copy of this file system. Changes to the shadow copy do not affect the
         * original, allowing multiple copies of the same core file system without multiple copies
         * of the same data.
         */ public shadow ( ignoreCase = this . ignoreCase ) { if ( ! this . isReadonly ) throw new Error ( "Cannot shadow a mutable file system." ) ; if ( ignoreCase && ! this . ignoreCase ) throw new Error ( "Cannot create a case-insensitive file system from a case-sensitive one." ) ; const fs = new FileSystem ( ignoreCase , { time : this . _time } ) ; fs . _shadowRoot = this ; fs . _cwd = this . _cwd ; return fs ; } link public time ( value ? ) { if ( value !== undefined && this . isReadonly ) throw createIOError ( "EPERM" ) ; let result = this . _time ; if ( typeof result === "function" ) result = result ( ) ; if ( typeof result === "object" ) result = result . getTime ( ) ; if ( result === - 1 ) result = Date . now ( ) ; if ( value !== undefined ) { this . _time = value ; } return result ; } param path public filemeta ( path ) { const { node } = this . _walk ( this . _resolve ( path ) ) ; if ( ! node ) throw createIOError ( "ENOENT" ) ; return this . _filemeta ( node ) ; } private _filemeta ( node ) { if ( ! node . meta ) { const parentMeta = node . shadowRoot && this . _shadowRoot && this . _shadowRoot . _filemeta ( node . shadowRoot ) ; node . meta = new collections . Metadata ( parentMeta ) ; } return node . meta ; } link public cwd ( ) { if ( ! this . _cwd ) throw new Error ( "The current working directory has not been set." ) ; const { node } = this . _walk ( this . _cwd ) ; if ( ! node ) throw createIOError ( "ENOENT" ) ; if ( ! isDirectory ( node ) ) throw createIOError ( "ENOTDIR" ) ; return this . _cwd ; } link public chdir ( path ) { if ( this . isReadonly ) throw createIOError ( "EPERM" ) ; path = this . _resolve ( path ) ; const { node } = this . _walk ( path ) ; if ( ! node ) throw createIOError ( "ENOENT" ) ; if ( ! isDirectory ( node ) ) throw createIOError ( "ENOTDIR" ) ; this . _cwd = path ; } /**
         * Pushes the current directory onto the directory stack and changes the current working directory to the supplied path.
         */ public pushd ( path ? ) { if ( this . isReadonly ) throw createIOError ( "EPERM" ) ; if ( path ) path = this . _resolve ( path ) ; if ( this . _cwd ) { if ( ! this . _dirStack ) this . _dirStack = [ ] ; this . _dirStack . push ( this . _cwd ) ; } if ( path && path !== this . _cwd ) { this . chdir ( path ) ; } } /**
         * Pops the previous directory from the location stack and changes the current directory to that directory.
         */ public popd ( ) { if ( this . isReadonly ) throw createIOError ( "EPERM" ) ; const path = this . _dirStack && this . _dirStack . pop ( ) ; if ( path ) { this . chdir ( path ) ; } } /**
         * Update the file system with a set of files.
         */ public apply ( files ) { this . _applyFiles ( files , this . _cwd ) ; } param path param axis param traversal public scanSync ( path , axis , traversal ) { path = this . _resolve ( path ) ; const results = [ ] ; this . _scan ( path , this . _stat ( this . _walk ( path ) ) , axis , traversal , false , results ) ; return results ; } param path param axis param traversal public lscanSync ( path , axis , traversal ) { path = this . _resolve ( path ) ; const results = [ ] ; this . _scan ( path , this . _stat ( this . _walk ( path , true ) ) , axis , traversal , true , results ) ; return results ; } private _scan ( path , stats , axis , traversal , noFollow , results ) { if ( axis === "ancestors-or-self" || axis === "self" || axis === "descendants-or-self" ) { if ( ! traversal . accept || traversal . accept ( path , stats ) ) { results . push ( path ) ; } } if ( axis === "ancestors-or-self" || axis === "ancestors" ) { const dirname = vpath . dirname ( path ) ; if ( dirname !== path ) { try { const stats = this . _stat ( this . _walk ( dirname , noFollow ) ) ; if ( ! traversal . traverse || traversal . traverse ( dirname , stats ) ) { this . _scan ( dirname , stats , "ancestors-or-self" , traversal , noFollow , results ) ; } } catch { } } } if ( axis === "descendants-or-self" || axis === "descendants" ) { if ( stats . isDirectory ( ) && ( ! traversal . traverse || traversal . traverse ( path , stats ) ) ) { for ( const file of this . readdirSync ( path ) ) { try { const childpath = vpath . combine ( path , file ) ; const stats = this . _stat ( this . _walk ( childpath , noFollow ) ) ; this . _scan ( childpath , stats , "descendants-or-self" , traversal , noFollow , results ) ; } catch { } } } } } param source param target param resolver public mountSync ( source , target , resolver ) { if ( this . isReadonly ) throw createIOError ( "EROFS" ) ; source = vpath . validate ( source , vpath . ValidationFlags . Absolute ) ; const { parent , links , node : existingNode , basename } = this . _walk ( this . _resolve ( target ) , true ) ; if ( existingNode ) throw createIOError ( "EEXIST" ) ; const time = this . time ( ) ; const node = this . _mknod ( parent ? parent . dev : ++ devCount , S_IFDIR , 0o777 , time ) ; node . source = source ; node . resolver = resolver ; this . _addLink ( parent , links , basename , node , time ) ; } /**
         * Recursively remove all files and directories underneath the provided path.
         */ public rimrafSync ( path ) { try { const stats = this . lstatSync ( path ) ; if ( stats . isFile ( ) || stats . isSymbolicLink ( ) ) { this . unlinkSync ( path ) ; } else if ( stats . isDirectory ( ) ) { for ( const file of this . readdirSync ( path ) ) { this . rimrafSync ( vpath . combine ( path , file ) ) ; } this . rmdirSync ( path ) ; } } catch ( e ) { if ( e . code === "ENOENT" ) return ; throw e ; } } /**
         * Make a directory and all of its parent paths (if they don't exist).
         */ public mkdirpSync ( path ) { path = this . _resolve ( path ) ; const result = this . _walk ( path , true , ( error , result ) => { if ( error . code === "ENOENT" ) { this . _mkdir ( result ) ; return "retry" ; } return "throw" ; } ) ; if ( ! result . node ) this . _mkdir ( result ) ; } public getFileListing ( ) { let result = "" ; const printLinks = ( dirname , links ) => { const iterator = collections . getIterator ( links ) ; try { for ( let i = collections . nextResult ( iterator ) ; i ; i = collections . nextResult ( iterator ) ) { const [ name , node ] = i . value ; const path = dirname ? vpath . combine ( dirname , name ) : name ; const marker = vpath . compare ( this . _cwd , path , this . ignoreCase ) === 0 ? "*" : " " ; if ( result ) result += "\n" ; result += marker ; if ( isDirectory ( node ) ) { result += vpath . addTrailingSeparator ( path ) ; printLinks ( path , this . _getLinks ( node ) ) ; } else if ( isFile ( node ) ) { result += path ; } else if ( isSymlink ( node ) ) { result += path + " -> " + node . symlink ; } } } finally { collections . closeIterator ( iterator ) ; } } ; printLinks ( undefined , this . _getRootLinks ( ) ) ; return result ; } /**
         * Print diagnostic information about the structure of the file system to the console.
         */ public debugPrint ( ) { console . log ( this . getFileListing ( ) ) ; } /**
         * Determines whether a path exists.
         */ public existsSync ( path ) { const result = this . _walk ( this . _resolve ( path ) , true , ( ) => "stop" ) ; return result !== undefined && result . node !== undefined ; } link public statSync ( path ) { return this . _stat ( this . _walk ( this . _resolve ( path ) ) ) ; } /**
         * Change file access times
         *
         * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
         */ public utimesSync ( path , atime , mtime ) { if ( this . isReadonly ) throw createIOError ( "EROFS" ) ; if ( ! isFinite ( + atime ) || ! isFinite ( + mtime ) ) throw createIOError ( "EINVAL" ) ; const entry = this . _walk ( this . _resolve ( path ) ) ; if ( ! entry || ! entry . node ) { throw createIOError ( "ENOENT" ) ; } entry . node . atimeMs = + atime ; entry . node . mtimeMs = + mtime ; entry . node . ctimeMs = this . time ( ) ; } link public lstatSync ( path ) { return this . _stat ( this . _walk ( this . _resolve ( path ) , true ) ) ; } private _stat ( entry ) { const node = entry . node ; if ( ! node ) throw createIOError ( `ENOENT` , entry . realpath ) ; return new Stats ( node . dev , node . ino , node . mode , node . nlink , 0 , isFile ( node ) ? this . _getSize ( node ) : isSymlink ( node ) ? node . symlink . length : 0 , 4096 , 0 , node . atimeMs , node . mtimeMs , node . ctimeMs , node . birthtimeMs ) ; } link public readdirSync ( path ) { const { node } = this . _walk ( this . _resolve ( path ) ) ; if ( ! node ) throw createIOError ( "ENOENT" ) ; if ( ! isDirectory ( node ) ) throw createIOError ( "ENOTDIR" ) ; return Array . from ( this . _getLinks ( node ) . keys ( ) ) ; } link public mkdirSync ( path ) { if ( this . isReadonly ) throw createIOError ( "EROFS" ) ; this . _mkdir ( this . _walk ( this . _resolve ( path ) , true ) ) ; } private _mkdir ( { parent , links , node : existingNode , basename } ) { if ( existingNode ) throw createIOError ( "EEXIST" ) ; const time = this . time ( ) ; const node = this . _mknod ( parent ? parent . dev : ++ devCount , S_IFDIR , 0o777 , time ) ; this . _addLink ( parent , links , basename , node , time ) ; } link public rmdirSync ( path ) { if ( this . isReadonly ) throw createIOError ( "EROFS" ) ; path = this . _resolve ( path ) ; const { parent , links , node , basename } = this . _walk ( path , true ) ; if ( ! parent ) throw createIOError ( "EPERM" ) ; if ( ! isDirectory ( node ) ) throw createIOError ( "ENOTDIR" ) ; if ( this . _getLinks ( node ) . size !== 0 ) throw createIOError ( "ENOTEMPTY" ) ; this . _removeLink ( parent , links , basename , node ) ; } link public linkSync ( oldpath , newpath ) { if ( this . isReadonly ) throw createIOError ( "EROFS" ) ; const { node } = this . _walk ( this . _resolve ( oldpath ) ) ; if ( ! node ) throw createIOError ( "ENOENT" ) ; if ( isDirectory ( node ) ) throw createIOError ( "EPERM" ) ; const { parent , links , basename , node : existingNode } = this . _walk ( this . _resolve ( newpath ) , true ) ; if ( ! parent ) throw createIOError ( "EPERM" ) ; if ( existingNode ) throw createIOError ( "EEXIST" ) ; this . _addLink ( parent , links , basename , node ) ; } link public unlinkSync ( path ) { if ( this . isReadonly ) throw createIOError ( "EROFS" ) ; const { parent , links , node , basename } = this . _walk ( this . _resolve ( path ) , true ) ; if ( ! parent ) throw createIOError ( "EPERM" ) ; if ( ! node ) throw createIOError ( "ENOENT" ) ; if ( isDirectory ( node ) ) throw createIOError ( "EISDIR" ) ; this . _removeLink ( parent , links , basename , node ) ; } link public renameSync ( oldpath , newpath ) { if ( this . isReadonly ) throw createIOError ( "EROFS" ) ; const { parent : oldParent , links : oldParentLinks , node , basename : oldBasename } = this . _walk ( this . _resolve ( oldpath ) , true ) ; if ( ! oldParent ) throw createIOError ( "EPERM" ) ; if ( ! node ) throw createIOError ( "ENOENT" ) ; const { parent : newParent , links : newParentLinks , node : existingNode , basename : newBasename } = this . _walk ( this . _resolve ( newpath ) , true ) ; if ( ! newParent ) throw createIOError ( "EPERM" ) ; const time = this . time ( ) ; if ( existingNode ) { if ( isDirectory ( node ) ) { if ( ! isDirectory ( existingNode ) ) throw createIOError ( "ENOTDIR" ) ; if ( this . stringComparer ( oldpath , newpath ) !== 0 && this . _getLinks ( existingNode ) . size > 0 ) throw createIOError ( "ENOTEMPTY" ) ; } else { if ( isDirectory ( existingNode ) ) throw createIOError ( "EISDIR" ) ; } this . _removeLink ( newParent , newParentLinks , newBasename , existingNode , time ) ; } this . _replaceLink ( oldParent , oldParentLinks , oldBasename , newParent , newParentLinks , newBasename , node , time ) ; } link public symlinkSync ( target , linkpath ) { if ( this . isReadonly ) throw createIOError ( "EROFS" ) ; const { parent , links , node : existingNode , basename } = this . _walk ( this . _resolve ( linkpath ) , true ) ; if ( ! parent ) throw createIOError ( "EPERM" ) ; if ( existingNode ) throw createIOError ( "EEXIST" ) ; const time = this . time ( ) ; const node = this . _mknod ( parent . dev , S_IFLNK , 0o666 , time ) ; node . symlink = vpath . validate ( target , vpath . ValidationFlags . RelativeOrAbsolute ) ; this . _addLink ( parent , links , basename , node , time ) ; } link public realpathSync ( path ) { const { realpath } = this . _walk ( this . _resolve ( path ) ) ; return realpath ; } /**
         * Read from a file.
         *
         * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
         */ public readFileSync ( path , encoding ? ) ; /**
         * Read from a file.
         *
         * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
         */ public readFileSync ( path , encoding ) ; /**
         * Read from a file.
         *
         * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
         */ public readFileSync ( path , encoding ? ) ; public readFileSync ( path , encoding = null ) { const { node } = this . _walk ( this . _resolve ( path ) ) ; if ( ! node ) throw createIOError ( "ENOENT" ) ; if ( isDirectory ( node ) ) throw createIOError ( "EISDIR" ) ; if ( ! isFile ( node ) ) throw createIOError ( "EBADF" ) ; const buffer = this . _getBuffer ( node ) . slice ( ) ; return encoding ? buffer . toString ( encoding ) : buffer ; } /**
         * Write to a file.
         *
         * NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
         */ public writeFileSync ( path , data , encoding = null ) { if ( this . isReadonly ) throw createIOError ( "EROFS" ) ; const { parent , links , node : existingNode , basename } = this . _walk ( this . _resolve ( path ) , false ) ; if ( ! parent ) throw createIOError ( "EPERM" ) ; const time = this . time ( ) ; let node = existingNode ; if ( ! node ) { node = this . _mknod ( parent . dev , S_IFREG , 0o666 , time ) ; this . _addLink ( parent , links , basename , node , time ) ; } if ( isDirectory ( node ) ) throw createIOError ( "EISDIR" ) ; if ( ! isFile ( node ) ) throw createIOError ( "EBADF" ) ; node . buffer = Buffer . isBuffer ( data ) ? data . slice ( ) : ts . sys . bufferFrom ! ( "" + data , encoding || "utf8" ) as Buffer ; node . size = node . buffer . byteLength ; node . mtimeMs = time ; node . ctimeMs = time ; } param base public diff ( base ? , options = { } ) { if ( ! base && ! options . baseIsNotShadowRoot ) base = this . shadowRoot ; const differences = { } ; const hasDifferences = base ? FileSystem . rootDiff ( differences , this , base , options ) : FileSystem . trackCreatedInodes ( differences , this , this . _getRootLinks ( ) ) ; return hasDifferences ? differences : undefined ; } /**
         * Generates a `FileSet` patch containing all the entries in `changed` that are not in `base`.
         */ public static diff ( changed , base , options = { } ) { const differences = { } ; return FileSystem . rootDiff ( differences , changed , base , options ) ? differences : undefined ; } private static diffWorker ( container , changed , changedLinks , base , baseLinks , options ) { if ( changedLinks && ! baseLinks ) return FileSystem . trackCreatedInodes ( container , changed , changedLinks ) ; if ( baseLinks && ! changedLinks ) return FileSystem . trackDeletedInodes ( container , baseLinks ) ; if ( changedLinks && baseLinks ) { let hasChanges = false ; baseLinks . forEach ( ( node , basename ) => { if ( ! changedLinks . has ( basename ) ) { container [ basename ] = isDirectory ( node ) ? new Rmdir ( ) : new Unlink ( ) ; hasChanges = true ; } } ) ; changedLinks . forEach ( ( changedNode , basename ) => { const baseNode = baseLinks . get ( basename ) ; if ( baseNode ) { if ( isDirectory ( changedNode ) && isDirectory ( baseNode ) ) { return hasChanges = FileSystem . directoryDiff ( container , basename , changed , changedNode , base , baseNode , options ) || hasChanges ; } if ( isFile ( changedNode ) && isFile ( baseNode ) ) { return hasChanges = FileSystem . fileDiff ( container , basename , changed , changedNode , base , baseNode , options ) || hasChanges ; } if ( isSymlink ( changedNode ) && isSymlink ( baseNode ) ) { return hasChanges = FileSystem . symlinkDiff ( container , basename , changedNode , baseNode ) || hasChanges ; } } return hasChanges = FileSystem . trackCreatedInode ( container , basename , changed , changedNode ) || hasChanges ; } ) ; return hasChanges ; } return false ; } private static rootDiff ( container , changed , base , options ) { while ( ! changed . _lazy . links && changed . _shadowRoot ) changed = changed . _shadowRoot ; while ( ! base . _lazy . links && base . _shadowRoot ) base = base . _shadowRoot ; if ( changed === base ) return false ; if ( ! changed . _lazy . links && ! changed . _shadowRoot && ! base . _lazy . links && ! base . _shadowRoot ) return false ; return FileSystem . diffWorker ( container , changed , changed . _getRootLinks ( ) , base , base . _getRootLinks ( ) , options ) ; } private static directoryDiff ( container , basename , changed , changedNode , base , baseNode , options ) { while ( ! changedNode . links && changedNode . shadowRoot ) changedNode = changedNode . shadowRoot ; while ( ! baseNode . links && baseNode . shadowRoot ) baseNode = baseNode . shadowRoot ; if ( changedNode === baseNode ) return false ; if ( isEmptyNonShadowedDirectory ( changedNode ) && isEmptyNonShadowedDirectory ( baseNode ) ) return false ; if ( ! changedNode . links && ! baseNode . links && changedNode . resolver && changedNode . source !== undefined && baseNode . resolver === changedNode . resolver && baseNode . source === changedNode . source ) return false ; const children = { } ; if ( ! FileSystem . diffWorker ( children , changed , changed . _getLinks ( changedNode ) , base , base . _getLinks ( baseNode ) , options ) ) { return false ; } container [ basename ] = new Directory ( children ) ; return true ; } private static fileDiff ( container , basename , changed , changedNode , base , baseNode , options ) { while ( ! changedNode . buffer && changedNode . shadowRoot ) changedNode = changedNode . shadowRoot ; while ( ! baseNode . buffer && baseNode . shadowRoot ) baseNode = baseNode . shadowRoot ; if ( changedNode === baseNode ) return false ; if ( isEmptyNonShadowedFile ( changedNode ) && isEmptyNonShadowedFile ( baseNode ) ) return false ; if ( ! changedNode . buffer && ! baseNode . buffer && changedNode . resolver && changedNode . source !== undefined && baseNode . resolver === changedNode . resolver && baseNode . source === changedNode . source ) return false ; const changedBuffer = changed . _getBuffer ( changedNode ) ; const baseBuffer = base . _getBuffer ( baseNode ) ; if ( changedBuffer === baseBuffer ) return false ; if ( Buffer . compare ( changedBuffer , baseBuffer ) === 0 ) { if ( ! options . includeChangedFileWithSameContent ) return false ; container [ basename ] = new SameFileContentFile ( changedBuffer ) ; return true ; } container [ basename ] = new File ( changedBuffer ) ; return true ; } private static symlinkDiff ( container , basename , changedNode , baseNode ) { if ( changedNode . symlink === baseNode . symlink ) return false ; container [ basename ] = new Symlink ( changedNode . symlink ) ; return true ; } private static trackCreatedInode ( container , basename , changed , node ) { if ( isDirectory ( node ) ) { const children = { } ; FileSystem . trackCreatedInodes ( children , changed , changed . _getLinks ( node ) ) ; container [ basename ] = new Directory ( children ) ; } else if ( isSymlink ( node ) ) { container [ basename ] = new Symlink ( node . symlink ) ; } else { container [ basename ] = new File ( node . buffer || "" ) ; } return true ; } private static trackCreatedInodes ( container , changed , changedLinks ) { if ( ! changedLinks . size ) return false ; changedLinks . forEach ( ( node , basename ) => { FileSystem . trackCreatedInode ( container , basename , changed , node ) ; } ) ; return true ; } private static trackDeletedInodes ( container , baseLinks ) { if ( ! baseLinks . size ) return false ; baseLinks . forEach ( ( node , basename ) => { container [ basename ] = isDirectory ( node ) ? new Rmdir ( ) : new Unlink ( ) ; } ) ; return true ; } private _mknod ( dev , type , mode , time ? ) ; private _mknod ( dev , type , mode , time ? ) ; private _mknod ( dev , type , mode , time ? ) ; private _mknod ( dev , type , mode , time = this . time ( ) ) { return { dev , ino : ++ inoCount , mode : ( mode & ~ S_IFMT & ~ 0o022 & 0o7777 ) | ( type & S_IFMT ) , atimeMs : time , mtimeMs : time , ctimeMs : time , birthtimeMs : time , nlink : 0 } as Inode ; } private _addLink ( parent , links , name , node , time = this . time ( ) ) { links . set ( name , node ) ; node . nlink ++ ; node . ctimeMs = time ; if ( parent ) parent . mtimeMs = time ; if ( ! parent && ! this . _cwd ) this . _cwd = name ; } private _removeLink ( parent , links , name , node , time = this . time ( ) ) { links . delete ( name ) ; node . nlink -- ; node . ctimeMs = time ; if ( parent ) parent . mtimeMs = time ; } private _replaceLink ( oldParent , oldLinks , oldName , newParent , newLinks , newName , node , time ) { if ( oldParent !== newParent ) { this . _removeLink ( oldParent , oldLinks , oldName , node , time ) ; this . _addLink ( newParent , newLinks , newName , node , time ) ; } else { oldLinks . delete ( oldName ) ; oldLinks . set ( newName , node ) ; oldParent . mtimeMs = time ; newParent . mtimeMs = time ; } } private _getRootLinks ( ) { if ( ! this . _lazy . links ) { this . _lazy . links = new collections . SortedMap < string , Inode > ( this . stringComparer ) ; if ( this . _shadowRoot ) { this . _copyShadowLinks ( this . _shadowRoot . _getRootLinks ( ) , this . _lazy . links ) ; } this . _lazy . links = this . _lazy . links ; } return this . _lazy . links ; } private _getLinks ( node ) { if ( ! node . links ) { const links = new collections . SortedMap < string , Inode > ( this . stringComparer ) ; const { source , resolver } = node ; if ( source && resolver ) { node . source = undefined ; node . resolver = undefined ; for ( const name of resolver . readdirSync ( source ) ) { const path = vpath . combine ( source , name ) ; const stats = resolver . statSync ( path ) ; switch ( stats . mode & S_IFMT ) { case S_IFDIR : const dir = this . _mknod ( node . dev , S_IFDIR , 0o777 ) ; dir . source = vpath . combine ( source , name ) ; dir . resolver = resolver ; this . _addLink ( node , links , name , dir ) ; break ; case S_IFREG : const file = this . _mknod ( node . dev , S_IFREG , 0o666 ) ; file . source = vpath . combine ( source , name ) ; file . resolver = resolver ; file . size = stats . size ; this . _addLink ( node , links , name , file ) ; break ; } } } else if ( this . _shadowRoot && node . shadowRoot ) { this . _copyShadowLinks ( this . _shadowRoot . _getLinks ( node . shadowRoot ) , links ) ; } node . links = links ; } return node . links ; } private _getShadow ( root ) ; private _getShadow ( root ) ; private _getShadow ( root ) { const shadows = this . _lazy . shadows || ( this . _lazy . shadows = new Map < number , Inode > ( ) ) ; let shadow = shadows . get ( root . ino ) ; if ( ! shadow ) { shadow = { dev : root . dev , ino : root . ino , mode : root . mode , atimeMs : root . atimeMs , mtimeMs : root . mtimeMs , ctimeMs : root . ctimeMs , birthtimeMs : root . birthtimeMs , nlink : root . nlink , shadowRoot : root } as Inode ; if ( isSymlink ( root ) ) ( shadow as SymlinkInode ) . symlink = root . symlink ; shadows . set ( shadow . ino , shadow ) ; } return shadow ; } private _copyShadowLinks ( source , target ) { const iterator = collections . getIterator ( source ) ; try { for ( let i = collections . nextResult ( iterator ) ; i ; i = collections . nextResult ( iterator ) ) { const [ name , root ] = i . value ; target . set ( name , this . _getShadow ( root ) ) ; } } finally { collections . closeIterator ( iterator ) ; } } private _getSize ( node ) { if ( node . buffer ) return node . buffer . byteLength ; if ( node . size !== undefined ) return node . size ; if ( node . source && node . resolver ) return node . size = node . resolver . statSync ( node . source ) . size ; if ( this . _shadowRoot && node . shadowRoot ) return node . size = this . _shadowRoot . _getSize ( node . shadowRoot ) ; return 0 ; } private _getBuffer ( node ) { if ( ! node . buffer ) { const { source , resolver } = node ; if ( source && resolver ) { node . source = undefined ; node . resolver = undefined ; node . size = undefined ; node . buffer = resolver . readFileSync ( source ) ; } else if ( this . _shadowRoot && node . shadowRoot ) { node . buffer = this . _shadowRoot . _getBuffer ( node . shadowRoot ) ; } else { node . buffer = Buffer . allocUnsafe ( 0 ) ; } } return node . buffer ; } param path param noFollow link private _walk ( path , noFollow ? , onError ? ) ; private _walk ( path , noFollow ? , onError ? ) ; private _walk ( path , noFollow ? , onError ? ) { let links = this . _getRootLinks ( ) ; let parent ; let components = vpath . parse ( path ) ; let step = 0 ; let depth = 0 ; let retry = false ; while ( true ) { if ( depth >= 40 ) throw createIOError ( "ELOOP" ) ; const lastStep = step === components . length - 1 ; let basename = components [ step ] ; const linkEntry = links . getEntry ( basename ) ; if ( linkEntry ) { components [ step ] = basename = linkEntry [ 0 ] ; } const node = linkEntry ?. [ 1 ] ; if ( lastStep && ( noFollow || ! isSymlink ( node ) ) ) { return { realpath : vpath . format ( components ) , basename , parent , links , node } ; } if ( node === undefined ) { if ( trapError ( createIOError ( "ENOENT" ) , node ) ) continue ; return undefined ; } if ( isSymlink ( node ) ) { const dirname = vpath . format ( components . slice ( 0 , step ) ) ; const symlink = vpath . resolve ( dirname , node . symlink ) ; links = this . _getRootLinks ( ) ; parent = undefined ; components = vpath . parse ( symlink ) . concat ( components . slice ( step + 1 ) ) ; step = 0 ; depth ++ ; retry = false ; continue ; } if ( isDirectory ( node ) ) { links = this . _getLinks ( node ) ; parent = node ; step ++ ; retry = false ; continue ; } if ( trapError ( createIOError ( "ENOTDIR" ) , node ) ) continue ; return undefined ; } function trapError ( error , node ? ) { const realpath = vpath . format ( components . slice ( 0 , step + 1 ) ) ; const basename = components [ step ] ; const result = ! retry && onError ? onError ( error , { realpath , basename , parent , links , node } ) : "throw" ; if ( result === "stop" ) return false ; if ( result === "retry" ) { retry = true ; return true ; } throw error ; } } /**
         * Resolve a path relative to the current working directory.
         */ private _resolve ( path ) { return this . _cwd ? vpath . resolve ( this . _cwd , vpath . validate ( path , vpath . ValidationFlags . RelativeOrAbsolute | vpath . ValidationFlags . AllowWildcard ) ) : vpath . validate ( path , vpath . ValidationFlags . Absolute | vpath . ValidationFlags . AllowWildcard ) ; } private _applyFiles ( files , dirname ) { const deferred = [ ] ; this . _applyFilesWorker ( files , dirname , deferred ) ; for ( const [ entry , path ] of deferred ) { this . mkdirpSync ( vpath . dirname ( path ) ) ; this . pushd ( vpath . dirname ( path ) ) ; if ( entry instanceof Symlink ) { if ( this . stringComparer ( vpath . dirname ( path ) , path ) === 0 ) { throw new TypeError ( "Roots cannot be symbolic links." ) ; } this . symlinkSync ( vpath . resolve ( dirname , entry . symlink ) , path ) ; this . _applyFileExtendedOptions ( path , entry ) ; } else if ( entry instanceof Link ) { if ( this . stringComparer ( vpath . dirname ( path ) , path ) === 0 ) { throw new TypeError ( "Roots cannot be hard links." ) ; } this . linkSync ( entry . path , path ) ; } else { this . mountSync ( entry . source , path , entry . resolver ) ; this . _applyFileExtendedOptions ( path , entry ) ; } this . popd ( ) ; } } private _applyFileExtendedOptions ( path , entry ) { const { meta } = entry ; if ( meta !== undefined ) { const filemeta = this . filemeta ( path ) ; for ( const key of Object . keys ( meta ) ) { filemeta . set ( key , meta [ key ] ) ; } } } private _applyFilesWorker ( files , dirname , deferred ) { for ( const key of Object . keys ( files ) ) { const value = normalizeFileSetEntry ( files [ key ] ) ; const path = dirname ? vpath . resolve ( dirname , key ) : key ; vpath . validate ( path , vpath . ValidationFlags . Absolute ) ; if ( value === null || value === undefined || value instanceof Rmdir || value instanceof Unlink ) { if ( this . stringComparer ( vpath . dirname ( path ) , path ) === 0 ) { throw new TypeError ( "Roots cannot be deleted." ) ; } this . rimrafSync ( path ) ; } else if ( value instanceof File ) { if ( this . stringComparer ( vpath . dirname ( path ) , path ) === 0 ) { throw new TypeError ( "Roots cannot be files." ) ; } this . mkdirpSync ( vpath . dirname ( path ) ) ; this . writeFileSync ( path , value . data , value . encoding ) ; this . _applyFileExtendedOptions ( path , value ) ; } else if ( value instanceof Directory ) { this . mkdirpSync ( path ) ; this . _applyFileExtendedOptions ( path , value ) ; this . _applyFilesWorker ( value . files , path , deferred ) ; } else { deferred . push ( [ value , path ] ) ; } } } } export interface FileSystemOptions { time ? : number | Date | ( ( ) => number | Date ) ; files ? : FileSet ; cwd ? : string ; meta ? : Record < string , any > ; } export interface FileSystemCreateOptions extends FileSystemOptions { documents ? : readonly documents . TextDocument [ ] ; } export type Axis = "ancestors" | "ancestors-or-self" | "self" | "descendants-or-self" | "descendants" ; export interface Traversal { /** A function called to choose whether to continue to traverse to either ancestors or descendants. */ traverse ? ( path , stats ) : boolean ; /** A function called to choose whether to accept a path as part of the result. */ accept ? ( path , stats ) : boolean ; } export interface FileSystemResolver { statSync ( path ) : { mode : number ; size : number ; } ; readdirSync ( path ) : string [ ] ; readFileSync ( path ) : Buffer ; } export interface FileSystemResolverHost { useCaseSensitiveFileNames ( ) : boolean ; getAccessibleFileSystemEntries ( path ) : ts . FileSystemEntries ; directoryExists ( path ) : boolean ; fileExists ( path ) : boolean ; getFileSize ( path ) : number ; readFile ( path ) : string | undefined ; getWorkspaceRoot ( ) : string ; } export function createResolver ( host ) { return { readdirSync ( path ) { const { files , directories } = host . getAccessibleFileSystemEntries ( path ) ; return directories . concat ( files ) ; } , statSync ( path ) { if ( host . directoryExists ( path ) ) { return { mode : S_IFDIR | 0o777 , size : 0 } ; } else if ( host . fileExists ( path ) ) { return { mode : S_IFREG | 0o666 , size : host . getFileSize ( path ) } ; } else { throw new Error ( "ENOENT: path does not exist" ) ; } } , readFileSync ( path ) { return ts . sys . bufferFrom ! ( host . readFile ( path ) ! , "utf8" ) as Buffer ; } } ; } /**
     * Create a virtual file system from a physical file system using the following path mappings:
     *
     *  - `/.ts` is a directory mapped to `${workspaceRoot}/built/local`
     *  - `/.lib` is a directory mapped to `${workspaceRoot}/tests/lib`
     *  - `/.src` is a virtual directory to be used for tests.
     *
     * Unless overridden, `/.src` will be the current working directory for the virtual file system.
     */ export function createFromFileSystem ( host , ignoreCase , { documents , files , cwd , time , meta } = { } ) { const fs = getBuiltLocal ( host , ignoreCase ) . shadow ( ) ; if ( meta ) { for ( const key of Object . keys ( meta ) ) { fs . meta . set ( key , meta [ key ] ) ; } } if ( time ) { fs . time ( time ) ; } if ( cwd ) { fs . mkdirpSync ( cwd ) ; fs . chdir ( cwd ) ; } if ( documents ) { for ( const document of documents ) { fs . mkdirpSync ( vpath . dirname ( document . file ) ) ; fs . writeFileSync ( document . file , document . text , "utf8" ) ; fs . filemeta ( document . file ) . set ( "document" , document ) ; const symlink = document . meta . get ( "symlink" ) ; if ( symlink ) { for ( const link of symlink . split ( "," ) . map ( link => link . trim ( ) ) ) { fs . mkdirpSync ( vpath . dirname ( link ) ) ; fs . symlinkSync ( vpath . resolve ( fs . cwd ( ) , document . file ) , link ) ; } } } } if ( files ) { fs . apply ( files ) ; } return fs ; } export class Stats { public dev : number ; public ino : number ; public mode : number ; public nlink : number ; public uid : number ; public gid : number ; public rdev : number ; public size : number ; public blksize : number ; public blocks : number ; public atimeMs : number ; public mtimeMs : number ; public ctimeMs : number ; public birthtimeMs : number ; public atime : Date ; public mtime : Date ; public ctime : Date ; public birthtime : Date ; constructor ( ) ; constructor ( dev , ino , mode , nlink , rdev , size , blksize , blocks , atimeMs , mtimeMs , ctimeMs , birthtimeMs ) ; constructor ( dev = 0 , ino = 0 , mode = 0 , nlink = 0 , rdev = 0 , size = 0 , blksize = 0 , blocks = 0 , atimeMs = 0 , mtimeMs = 0 , ctimeMs = 0 , birthtimeMs = 0 ) { this . dev = dev ; this . ino = ino ; this . mode = mode ; this . nlink = nlink ; this . uid = 0 ; this . gid = 0 ; this . rdev = rdev ; this . size = size ; this . blksize = blksize ; this . blocks = blocks ; this . atimeMs = atimeMs ; this . mtimeMs = mtimeMs ; this . ctimeMs = ctimeMs ; this . birthtimeMs = birthtimeMs ; this . atime = new Date ( this . atimeMs ) ; this . mtime = new Date ( this . mtimeMs ) ; this . ctime = new Date ( this . ctimeMs ) ; this . birthtime = new Date ( this . birthtimeMs ) ; } public isFile ( ) { return ( this . mode & S_IFMT ) === S_IFREG ; } public isDirectory ( ) { return ( this . mode & S_IFMT ) === S_IFDIR ; } public isSymbolicLink ( ) { return ( this . mode & S_IFMT ) === S_IFLNK ; } public isBlockDevice ( ) { return ( this . mode & S_IFMT ) === S_IFBLK ; } public isCharacterDevice ( ) { return ( this . mode & S_IFMT ) === S_IFCHR ; } public isFIFO ( ) { return ( this . mode & S_IFMT ) === S_IFIFO ; } public isSocket ( ) { return ( this . mode & S_IFMT ) === S_IFSOCK ; } } export const IOErrorMessages = Object . freeze ( { EACCES : "access denied" , EIO : "an I/O error occurred" , ENOENT : "no such file or directory" , EEXIST : "file already exists" , ELOOP : "too many symbolic links encountered" , ENOTDIR : "no such directory" , EISDIR : "path is a directory" , EBADF : "invalid file descriptor" , EINVAL : "invalid value" , ENOTEMPTY : "directory not empty" , EPERM : "operation not permitted" , EROFS : "file system is read-only" } ) ; export function createIOError ( code , details = "" ) { const err = new Error ( `${ code }: ${ IOErrorMessages [ code ] } ${ details }` ) ; err . code = code ; if ( Error . captureStackTrace ) Error . captureStackTrace ( err , createIOError ) ; return err ; } /**
     * A template used to populate files, directories, links, etc. in a virtual file system.
     */ export interface FileSet { [ name ] : DirectoryLike | FileLike | Link | Symlink | Mount | Rmdir | Unlink | null | undefined ; } export type DirectoryLike = FileSet | Directory ; export type FileLike = File | Buffer | string ; /** Extended options for a directory in a `FileSet` */ export class Directory { public readonly files : FileSet ; public readonly meta : Record < string , any > | undefined ; constructor ( files , { meta } = { } ) { this . files = files ; this . meta = meta ; } } /** Extended options for a file in a `FileSet` */ export class File { public readonly data : Buffer | string ; public readonly encoding : string | undefined ; public readonly meta : Record < string , any > | undefined ; constructor ( data , { meta , encoding } = { } ) { this . data = data ; this . encoding = encoding ; this . meta = meta ; } } export class SameFileContentFile extends File { constructor ( data , metaAndEncoding ? ) { super ( data , metaAndEncoding ) ; } } /** Extended options for a hard link in a `FileSet` */ export class Link { public readonly path : string ; constructor ( path ) { this . path = path ; } } /** Removes a directory in a `FileSet` */ export class Rmdir { public _rmdirBrand ? : never ; } /** Unlinks a file in a `FileSet` */ export class Unlink { public _unlinkBrand ? : never ; } /** Extended options for a symbolic link in a `FileSet` */ export class Symlink { public readonly symlink : string ; public readonly meta : Record < string , any > | undefined ; constructor ( symlink , { meta } = { } ) { this . symlink = symlink ; this . meta = meta ; } } /** Extended options for mounting a virtual copy of an external file system via a `FileSet` */ export class Mount { public readonly source : string ; public readonly resolver : FileSystemResolver ; public readonly meta : Record < string , any > | undefined ; constructor ( source , resolver , { meta } = { } ) { this . source = source ; this . resolver = resolver ; this . meta = meta ; } } type Inode = FileInode | DirectoryInode | SymlinkInode ; interface FileInode { dev : number ; ino : number ; mode : number ; atimeMs : number ; mtimeMs : number ; ctimeMs : number ; birthtimeMs : number ; nlink : number ; size ? : number ; buffer ? : Buffer ; source ? : string ; resolver ? : FileSystemResolver ; shadowRoot ? : FileInode ; meta ? : collections . Metadata ; } interface DirectoryInode { dev : number ; ino : number ; mode : number ; atimeMs : number ; mtimeMs : number ; ctimeMs : number ; birthtimeMs : number ; nlink : number ; links ? : collections . SortedMap < string , Inode > ; source ? : string ; resolver ? : FileSystemResolver ; shadowRoot ? : DirectoryInode ; meta ? : collections . Metadata ; } interface SymlinkInode { dev : number ; ino : number ; mode : number ; atimeMs : number ; mtimeMs : number ; ctimeMs : number ; birthtimeMs : number ; nlink : number ; symlink : string ; shadowRoot ? : SymlinkInode ; meta ? : collections . Metadata ; } function isEmptyNonShadowedDirectory ( node ) { return ! node . links && ! node . shadowRoot && ! node . resolver && ! node . source ; } function isEmptyNonShadowedFile ( node ) { return ! node . buffer && ! node . shadowRoot && ! node . resolver && ! node . source ; } function isFile ( node ) { return node !== undefined && ( node . mode & S_IFMT ) === S_IFREG ; } function isDirectory ( node ) { return node !== undefined && ( node . mode & S_IFMT ) === S_IFDIR ; } function isSymlink ( node ) { return node !== undefined && ( node . mode & S_IFMT ) === S_IFLNK ; } interface WalkResult { realpath : string ; basename : string ; parent : DirectoryInode | undefined ; links : collections . SortedMap < string , Inode > ; node : Inode | undefined ; } let builtLocalHost ; let builtLocalCI ; let builtLocalCS ; function getBuiltLocal ( host , ignoreCase ) { if ( builtLocalHost !== host ) { builtLocalCI = undefined ; builtLocalCS = undefined ; builtLocalHost = host ; } if ( ! builtLocalCI ) { const resolver = createResolver ( host ) ; builtLocalCI = new FileSystem ( true , { files : { [ builtFolder ] : new Mount ( vpath . resolve ( host . getWorkspaceRoot ( ) , "built/local" ) , resolver ) , [ testLibFolder ] : new Mount ( vpath . resolve ( host . getWorkspaceRoot ( ) , "tests/lib" ) , resolver ) , [ projectsFolder ] : new Mount ( vpath . resolve ( host . getWorkspaceRoot ( ) , "tests/projects" ) , resolver ) , [ srcFolder ] : { } } , cwd : srcFolder , meta : { defaultLibLocation : builtFolder } } ) ; builtLocalCI . makeReadonly ( ) ; } if ( ignoreCase ) return builtLocalCI ; if ( ! builtLocalCS ) { builtLocalCS = builtLocalCI . shadow ( false ) ; builtLocalCS . makeReadonly ( ) ; } return builtLocalCS ; } function normalizeFileSetEntry ( value ) { if ( value === undefined || value === null || value instanceof Directory || value instanceof File || value instanceof Link || value instanceof Symlink || value instanceof Mount || value instanceof Rmdir || value instanceof Unlink ) { return value ; } return typeof value === "string" || Buffer . isBuffer ( value ) ? new File ( value ) : new Directory ( value ) ; } export function formatPatch ( patch ) ; export function formatPatch ( patch ) ; export function formatPatch ( patch ) { return patch ? formatPatchWorker ( "" , patch ) : null ; } function formatPatchWorker ( dirname , container ) { let text = "" ; for ( const name of Object . keys ( container ) ) { const entry = normalizeFileSetEntry ( container [ name ] ) ; const file = dirname ? vpath . combine ( dirname , name ) : name ; if ( entry === null || entry === undefined || entry instanceof Unlink || entry instanceof Rmdir ) { text += `//// [${ file }] unlink\r\n` ; } else if ( entry instanceof Rmdir ) { text += `//// [${ vpath . addTrailingSeparator ( file ) }] rmdir\r\n` ; } else if ( entry instanceof Directory ) { text += formatPatchWorker ( file , entry . files ) ; } else if ( entry instanceof SameFileContentFile ) { text += `//// [${ file }] file written with same contents\r\n` ; } else if ( entry instanceof File ) { const content = typeof entry . data === "string" ? entry . data : entry . data . toString ( "utf8" ) ; text += `//// [${ file }]\r\n${ content }\r\n\r\n` ; } else if ( entry instanceof Link ) { text += `//// [${ file }] link(${ entry . path })\r\n` ; } else if ( entry instanceof Symlink ) { text += `//// [${ file }] symlink(${ entry . symlink })\r\n` ; } else if ( entry instanceof Mount ) { text += `//// [${ file }] mount(${ entry . source })\r\n` ; } } return text ; } export function iteratePatch ( patch ) { return patch ? Harness . Compiler . iterateOutputs ( iteratePatchWorker ( "" , patch ) ) : null ; } function * iteratePatchWorker ( dirname , container ) { for ( const name of Object . keys ( container ) ) { const entry = normalizeFileSetEntry ( container [ name ] ) ; const file = dirname ? vpath . combine ( dirname , name ) : name ; if ( entry instanceof Directory ) { yield * ts . arrayFrom ( iteratePatchWorker ( file , entry . files ) ) ; } else if ( entry instanceof File ) { const content = typeof entry . data === "string" ? entry . data : entry . data . toString ( "utf8" ) ; yield new documents . TextDocument ( file , content ) ; } } } }