import path from "path" ; import Immutable from "immutable" ; import stream from "stream" ; import File from "./file" ; import Promise from "../utils/promise" ; import error from "../utils/error" ; import PathUtil from "../utils/path" ; import { root } from "@honkit/html/lib/dom" ; class FS extends Immutable . Record ( { root : String ( ) , fsExists : Function ( ) , fsReadFile : Function ( ) , fsStatFile : Function ( ) , fsReadDir : Function ( ) , fsLoadObject : null , fsReadAsStream : null , } ) { return { string } getRoot ( ) { return this . get ( "root" ) ; } param { string } filename return { boolean } isInScope ( filename ) { const rootPath = this . getRoot ( ) ; filename = path . join ( rootPath , filename ) ; return PathUtil . isInRoot ( rootPath , filename ) ; } return { string } resolve ( ... args ) { const rootPath = this . getRoot ( ) ; let filename = path . join . apply ( path , [ rootPath ] . concat ( args ) ) ; filename = path . normalize ( filename ) ; if ( ! this . isInScope ( filename ) ) { throw error . FileOutOfScopeError ( { filename : filename , root : rootPath , } ) ; } return filename ; } param { string } filename return { Promise < Boolean > } exists ( filename ) { const that = this ; return Promise ( ) . then ( ( ) => { filename = that . resolve ( filename ) ; const exists = that . get ( "fsExists" ) ; return exists ( filename ) ; } ) ; } param { string } filename return { Promise < Buffer > } read ( filename ) { const that = this ; return Promise ( ) . then ( ( ) => { filename = that . resolve ( filename ) ; const read = that . get ( "fsReadFile" ) ; return read ( filename ) ; } ) ; } return { Promise < String > } readAsString ( filename , encoding = "utf8" ) { return this . read ( filename ) . then ( ( buf ) => { return buf . toString ( encoding ) ; } ) ; } param { string } filename return { Promise < Stream > } readAsStream ( filename ) { const that = this ; const filepath = that . resolve ( filename ) ; const fsReadAsStream = this . get ( "fsReadAsStream" ) ; if ( fsReadAsStream ) { return Promise ( fsReadAsStream ( filepath ) ) ; } return this . read ( filename ) . then ( ( buf ) => { const bufferStream = new stream . PassThrough ( ) ; bufferStream . end ( buf ) ; return bufferStream ; } ) ; } param { string } filename return { Promise < File > } statFile ( filename ) { const that = this ; return Promise ( ) . then ( ( ) => { const filepath = that . resolve ( filename ) ; const stat = that . get ( "fsStatFile" ) ; return stat ( filepath ) ; } ) . then ( ( stat ) => { return File . createFromStat ( filename , stat ) ; } ) ; } param { string } dirname return { Promise < List < String > > } readDir ( dirname ) { const that = this ; return Promise ( ) . then ( ( ) => { const dirpath = that . resolve ( dirname ) ; const readDir = that . get ( "fsReadDir" ) ; return readDir ( dirpath ) ; } ) . then ( ( files ) => { return Immutable . List ( files ) ; } ) ; } param { string } dirname return { Promise < List < String > > } listFiles ( dirname ) { return this . readDir ( dirname ) . then ( ( files ) => { return files . filterNot ( pathIsFolder ) ; } ) ; } param { string } dirName param { Function return { Promise < List < String > > } listAllFiles ( dirName , filterFn ) { const that = this ; dirName = dirName || "." ; return this . readDir ( dirName ) . then ( ( files ) => { return Promise . reduce ( files , ( out , file ) => { const isDirectory = pathIsFolder ( file ) ; const newDirName = path . join ( dirName , file ) ; if ( filterFn && filterFn ( newDirName ) === false ) { return out ; } if ( ! isDirectory ) { return out . push ( newDirName ) ; } return that . listAllFiles ( newDirName , filterFn ) . then ( ( inner ) => { return out . concat ( inner ) ; } ) ; } , Immutable . List ( ) ) ; } ) ; } param { string } dirname param { string } filename return { Promise < String > } findFile ( dirname , filename ) { return this . listFiles ( dirname ) . then ( ( files ) => { return files . find ( ( file ) => { return file . toLowerCase ( ) == filename . toLowerCase ( ) ; } ) ; } ) ; } param { string } filename return { Promise < Object > } loadAsObject ( filename ) { const that = this ; const fsLoadObject = this . get ( "fsLoadObject" ) ; return this . exists ( filename ) . then ( ( exists ) => { if ( ! exists ) { const err = new Error ( "Module doesn't exist" ) ; err . code = "MODULE_NOT_FOUND" ; throw err ; } if ( fsLoadObject ) { return fsLoadObject ( that . resolve ( filename ) ) ; } else { return that . readAsString ( filename ) . then ( ( str ) => { return JSON . parse ( str ) ; } ) ; } } ) ; } param { Object } def return { FS } static create ( def ) { return new FS ( def ) ; } param { FS } fs param { string } scope return { FS } static reduceScope ( fs , scope ) { return fs . set ( "root" , path . join ( fs . getRoot ( ) , scope ) ) as FS ; } } function pathIsFolder ( filename ) { const lastChar = filename [ filename . length - 1 ] ; return lastChar == "/" || lastChar == "\\" ; } export default FS ;