'use strict' ; import fetch from 'node-fetch' ; import { Disposable , Event , EventEmitter , FileChangeEvent , FileStat , FileSystemError , FileSystemProvider , FileType , Uri , workspace } from 'vscode' ; import { fileSystemScheme } from './constants' ; import { GitHubApi } from './gitHubApi' ; import { Strings } from './system' ; import { fromRemoteHubUri } from './uris' ; export class GitHubFileSystemProvider implements FileSystemProvider , Disposable { private readonly _disposable : Disposable ; private _fsCache = new Map < string , any > ( ) ; constructor ( private readonly _github ) { this . _disposable = Disposable . from ( workspace . registerFileSystemProvider ( fileSystemScheme , this , { isCaseSensitive : true , isReadonly : true } ) ) ; } dispose ( ) { this . _disposable && this . _disposable . dispose ( ) ; } private _onDidChangeFile = new EventEmitter < FileChangeEvent [ ] > ( ) ; get onDidChangeFile ( ) : Event < FileChangeEvent [ ] > { return this . _onDidChangeFile . event ; } watch ( ) { return { dispose : ( ) => { } } ; } async stat ( uri ) { if ( uri . path === '' || uri . path . lastIndexOf ( '/' ) === 0 ) { return { type : FileType . Directory , size : 0 , ctime : 0 , mtime : 0 } ; } const data = await this . fsQuery < { __typename : string ; byteSize : number | undefined ; } > ( uri , `__typename
            ...on Blob {
                byteSize
            }` , this . _fsCache ) ; return { type : GitHubFileSystemProvider . typeToFileType ( data && data . __typename ) , size : ( data && data . byteSize ) || 0 , ctime : 0 , mtime : 0 } ; } async readDirectory ( uri ) { const data = await this . fsQuery < { entries : { name : string ; type : string ; } [ ] ; } > ( uri , `... on Tree {
                entries {
                    name
                    type
                }
            }` , this . _fsCache ) ; return ( ( data && data . entries ) || [ ] ) . map < [ string , FileType ] > ( e => [ e . name , GitHubFileSystemProvider . typeToFileType ( e . type ) ] ) ; } createDirectory ( ) { throw FileSystemError . NoPermissions ; } async readFile ( uri ) { const data = await this . fsQuery < { oid : string ; isBinary : boolean ; text : string ; } > ( uri , `... on Blob {
                oid,
                isBinary,
                text
            }` ) ; if ( data ) { this . _github . setRevisionForUri ( uri , data . oid ) ; } if ( data && data . isBinary ) { const [ owner , repo , path ] = fromRemoteHubUri ( uri ) ; const downloadUri = uri . with ( { scheme : 'https' , authority : 'raw.githubusercontent.com' , path : `/${ owner }/${ repo }/HEAD/${ path }` } ) ; return GitHubFileSystemProvider . downloadBinary ( downloadUri ) ; } return Buffer . from ( ( data && data . text ) || '' ) ; } writeFile ( ) { throw FileSystemError . NoPermissions ; } delete ( ) { throw FileSystemError . NoPermissions ; } rename ( ) { throw FileSystemError . NoPermissions ; } copy ? ( ) { throw FileSystemError . NoPermissions ; } private async fsQuery < T > ( uri , query , cache ? ) { if ( cache === undefined ) { return this . _github . fsQuery < T > ( uri , query ) ; } const key = `${ uri . toString ( ) }:${ Strings . sha1 ( query ) }` ; let data = cache . get ( key ) ; if ( data !== undefined ) return data as T ; data = await this . _github . fsQuery < T > ( uri , query ) ; cache . set ( key , data ) ; return data ; } private static async downloadBinary ( uri ) { const resp = await fetch ( uri . toString ( ) ) ; return resp . buffer ( ) ; } private static typeToFileType ( type ) { if ( type ) { type = type . toLocaleLowerCase ( ) ; } switch ( type ) { case 'blob' : return FileType . File ; case 'tree' : return FileType . Directory ; default : return FileType . Unknown ; } } }