11import { resolve as resolveCrossPlatform } from 'path' ;
22import * as pathModule from 'path' ;
3- import { Node , Link , File , Stats } from "./node" ;
3+ import { Node , Link , File , Stats , Dirent } from "./node" ;
44import { Buffer } from 'buffer' ;
55import setImmediate from './setImmediate' ;
66import process from './process' ;
77import setTimeoutUnref , { TSetTimeout } from "./setTimeoutUnref" ;
88import { Readable , Writable } from 'stream' ;
99import { constants } from "./constants" ;
1010import { EventEmitter } from "events" ;
11+ import { TEncoding , TEncodingExtended , TDataOut , assertEncoding , strToEncoding , ENCODING_UTF8 } from './encoding' ;
1112import errors = require( './internal/errors' ) ;
1213import extend = require( 'fast-extend' ) ;
1314import util = require( 'util' ) ;
@@ -41,12 +42,9 @@ export interface IError extends Error {
4142
4243export type TFilePath = string | Buffer | URL ;
4344export type TFileId = TFilePath | number ; // Number is used as a file descriptor.
44- export type TDataOut = string | Buffer ; // Data formats we give back to users.
4545export type TData = TDataOut | Uint8Array ; // Data formats users can give us.
4646export type TFlags = string | number ;
4747export type TMode = string | number ; // Mode can be a String, although docs say it should be a Number.
48- export type TEncoding = 'ascii' | 'utf8' | 'utf16le' | 'ucs2' | 'base64' | 'latin1' | 'binary' | 'hex' ;
49- export type TEncodingExtended = TEncoding | 'buffer' ;
5048export type TTime = number | string | Date ;
5149export type TCallback < TData > = ( error ?: IError , data ?: TData ) => void ;
5250// type TCallbackWrite = (err?: IError, bytesWritten?: number, source?: Buffer) => void;
@@ -55,8 +53,6 @@ export type TCallback<TData> = (error?: IError, data?: TData) => void;
5553
5654// ---------------------------------------- Constants
5755
58- const ENCODING_UTF8 : TEncoding = 'utf8' ;
59-
6056// Default modes for opening files.
6157const enum MODE {
6258 FILE = 0o666 ,
@@ -198,11 +194,6 @@ export function flagsToNumber(flags: TFlags): number {
198194
199195// ---------------------------------------- Options
200196
201- function assertEncoding ( encoding : string ) {
202- if ( encoding && ! Buffer . isEncoding ( encoding ) )
203- throw new errors . TypeError ( 'ERR_INVALID_OPT_VALUE_ENCODING' , encoding ) ;
204- }
205-
206197function getOptions < T extends IOptions > ( defaults : T , options ?: T | string ) : T {
207198 let opts : T ;
208199 if ( ! options ) return defaults ;
@@ -349,6 +340,16 @@ const getMkdirOptions = options => {
349340 return extend ( { } , mkdirDefaults , options ) ;
350341}
351342
343+ // Options for `fs.readdir` and `fs.readdirSync`
344+ export interface IReaddirOptions extends IOptions {
345+ withFileTypes ?: boolean ,
346+ } ;
347+ const readdirDefaults : IReaddirOptions = {
348+ encoding : 'utf8' ,
349+ withFileTypes : false ,
350+ } ;
351+ const getReaddirOptions = optsGenerator < IReaddirOptions > ( readdirDefaults ) ;
352+ const getReaddirOptsAndCb = optsAndCbGenerator < IReaddirOptions , TDataOut [ ] | Dirent [ ] > ( getReaddirOptions ) ;
352353
353354
354355// ---------------------------------------- Utility functions
@@ -420,12 +421,6 @@ export function dataToBuffer(data: TData, encoding: string = ENCODING_UTF8): Buf
420421 else return Buffer . from ( String ( data ) , encoding ) ;
421422}
422423
423- export function strToEncoding ( str : string , encoding ?: TEncodingExtended ) : TDataOut {
424- if ( ! encoding || ( encoding === ENCODING_UTF8 ) ) return str ; // UTF-8
425- if ( encoding === 'buffer' ) return new Buffer ( str ) ; // `buffer` encoding
426- return ( new Buffer ( str ) ) . toString ( encoding ) ; // Custom encoding
427- }
428-
429424export function bufferToEncoding ( buffer : Buffer , encoding ?: TEncodingExtended ) : TDataOut {
430425 if ( ! encoding || ( encoding === 'buffer' ) ) return buffer ;
431426 else return buffer . toString ( encoding ) ;
@@ -1567,7 +1562,7 @@ export class Volume {
15671562 this . writeFile ( id , data , opts , callback ) ;
15681563 }
15691564
1570- private readdirBase ( filename : string , encoding : TEncodingExtended ) : TDataOut [ ] {
1565+ private readdirBase ( filename : string , options : IReaddirOptions ) : TDataOut [ ] | Dirent [ ] {
15711566 const steps = filenameToSteps ( filename ) ;
15721567 const link : Link = this . getResolvedLink ( steps ) ;
15731568 if ( ! link ) throwError ( ENOENT , 'readdir' , filename ) ;
@@ -1576,35 +1571,41 @@ export class Volume {
15761571 if ( ! node . isDirectory ( ) )
15771572 throwError ( ENOTDIR , 'scandir' , filename ) ;
15781573
1574+ if ( options . withFileTypes ) {
1575+ const list : Dirent [ ] = [ ] ;
1576+ for ( let name in link . children ) {
1577+ list . push ( Dirent . build ( link . children [ name ] , options . encoding ) ) ;
1578+ }
1579+ if ( ! isWin && options . encoding !== 'buffer' ) list . sort ( ( a , b ) => {
1580+ if ( a . name < b . name ) return - 1 ;
1581+ if ( a . name > b . name ) return 1 ;
1582+ return 0 ;
1583+ } ) ;
1584+ return list ;
1585+ }
1586+
15791587 const list : TDataOut [ ] = [ ] ;
1580- for ( let name in link . children )
1581- list . push ( strToEncoding ( name , encoding ) ) ;
1588+ for ( let name in link . children ) {
1589+ list . push ( strToEncoding ( name , options . encoding ) ) ;
1590+ }
15821591
1583- if ( ! isWin && encoding !== 'buffer' ) list . sort ( ) ;
1592+ if ( ! isWin && options . encoding !== 'buffer' ) list . sort ( ) ;
15841593
15851594 return list ;
15861595 }
15871596
1588- readdirSync ( path : TFilePath , options ?: IOptions | string ) : TDataOut [ ] {
1589- const opts = getDefaultOpts ( options ) ;
1597+ readdirSync ( path : TFilePath , options ?: IReaddirOptions | string ) : TDataOut [ ] | Dirent [ ] {
1598+ const opts = getReaddirOptions ( options ) ;
15901599 const filename = pathToFilename ( path ) ;
1591- return this . readdirBase ( filename , opts . encoding ) ;
1600+ return this . readdirBase ( filename , opts ) ;
15921601 }
15931602
1594- readdir ( path : TFilePath , callback : TCallback < TDataOut [ ] > ) ;
1595- readdir ( path : TFilePath , options : IOptions | string , callback : TCallback < TDataOut [ ] > ) ;
1603+ readdir ( path : TFilePath , callback : TCallback < TDataOut [ ] | Dirent [ ] > ) ;
1604+ readdir ( path : TFilePath , options : IReaddirOptions | string , callback : TCallback < TDataOut [ ] | Dirent [ ] > ) ;
15961605 readdir ( path : TFilePath , a ?, b ?) {
1597- let options : IOptions | string = a ;
1598- let callback : TCallback < TDataOut [ ] > = b ;
1599-
1600- if ( typeof a === 'function' ) {
1601- callback = a ;
1602- options = optsDefaults ;
1603- }
1604-
1605- const opts = getDefaultOpts ( options ) ;
1606+ const [ options , callback ] = getReaddirOptsAndCb ( a , b ) ;
16061607 const filename = pathToFilename ( path ) ;
1607- this . wrapAsync ( this . readdirBase , [ filename , opts . encoding ] , callback ) ;
1608+ this . wrapAsync ( this . readdirBase , [ filename , options ] , callback ) ;
16081609 }
16091610
16101611 private readlinkBase ( filename : string , encoding : TEncodingExtended ) : TDataOut {
0 commit comments