readdir-glob
Version:
Recursive fs.readdir with streaming API and glob filtering.
1 lines • 14.9 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":["dir: fs.PathLike","strict: boolean","resolve","file: fs.PathLike","followSymlinks: boolean","dir: string","path: string","useStat:boolean","shouldSkip:(path: string) => boolean","strict:boolean","name: string","stat: Stat","useStat: boolean","shouldSkip: (path: string) => boolean","options: Options","cwd?: string","options?: Options | Callback","cb?: Callback","matches: string[]","relative: string","isDirectory: boolean","err: NodeJS.ErrnoException","readdirGlob: readdirGlobInterface","pattern?: string","options?: Options | Callback"],"sources":["../src/index.ts"],"sourcesContent":["import * as fs from 'fs';\nimport { EventEmitter } from 'events';\nimport { Minimatch } from 'minimatch';\nimport { resolve } from 'path';\n\nfunction readdir(dir: fs.PathLike, strict: boolean): Promise<fs.Dirent[]> {\n return new Promise((resolve, reject) => {\n fs.readdir(dir, {withFileTypes: true} ,(err, files) => {\n if(err) {\n switch (err.code) {\n case 'ENOTDIR': // Not a directory\n if(strict) {\n reject(err);\n } else {\n resolve([]);\n }\n break;\n case 'ENOTSUP': // Operation not supported\n case 'ENOENT': // No such file or directory\n case 'ENAMETOOLONG': // Filename too long\n case 'UNKNOWN':\n resolve([]);\n break;\n case 'ELOOP': // Too many levels of symbolic links\n default:\n reject(err);\n break;\n }\n } else {\n resolve(files);\n }\n });\n });\n}\n\nfunction getStat(file: fs.PathLike, followSymlinks: boolean): Promise<fs.Stats | null> {\n return new Promise((resolve) => {\n const statFunc = followSymlinks ? fs.stat : fs.lstat;\n statFunc(file, (err, stats) => {\n if(err) {\n switch (err.code) {\n case 'ENOENT':\n if(followSymlinks) {\n // Fallback to lstat to handle broken links as files\n resolve(getStat(file, false)); \n } else {\n resolve(null);\n }\n break;\n default:\n resolve(null);\n break;\n }\n } else {\n resolve(stats);\n }\n });\n });\n}\n\nexport type Stat = fs.Dirent | fs.Stats;\nexport type Match = {\n relative: string,\n absolute: string,\n stat?: Stat\n};\n\nasync function* exploreWalkAsync(\n dir: string,\n path: string,\n followSymlinks: boolean,\n useStat:boolean,\n shouldSkip:(path: string) => boolean,\n strict:boolean\n) : AsyncGenerator<Required<Match>> {\n let files = await readdir(path + dir, strict);\n for(const file of files) {\n let name: string = file.name;\n const filename = dir + '/' + name;\n const relative = filename.slice(1); // Remove the leading /\n const absolute = path + '/' + relative;\n let stat: Stat = file;\n if(useStat || followSymlinks) {\n stat = await getStat(absolute, followSymlinks) ?? stat;\n }\n if(stat.isDirectory()) {\n if(!shouldSkip(relative)) {\n yield {relative, absolute, stat};\n yield* exploreWalkAsync(filename, path, followSymlinks, useStat, shouldSkip, false);\n }\n } else {\n yield {relative, absolute, stat};\n }\n }\n}\n\n\nasync function* explore(\n path: string,\n followSymlinks: boolean,\n useStat: boolean,\n shouldSkip: (path: string) => boolean\n): AsyncGenerator<Required<Match>> {\n yield* exploreWalkAsync('', path, followSymlinks, useStat, shouldSkip, true);\n}\n\n\nexport type Options = {\n /**\n * Glob pattern or Array of Glob patterns to match the found files with.\n * A file has to match at least one of the provided patterns to be returned.\n */\n pattern?: string | string[];\n /**\n * Allow pattern to match filenames starting with a period, even if the pattern\n * does not explicitly have a period in that spot.\n */\n dot?: boolean;\n /**\n * Disable `**` matching against multiple folder names.\n */\n noglobstar?: boolean;\n /**\n * Perform a basename-only match if the pattern does not contain any slash\n * characters. That is, `*.js` would be treated as equivalent to `**\\/*.js`,\n * matching all js files in all directories.\n */\n matchBase?: boolean;\n /**\n * Perform a case-insensitive match. Note: on case-insensitive file systems,\n * non-magic patterns will match by default, since `stat` and `readdir` will\n * not raise errors.\n */\n nocase?: boolean;\n /**\n * Glob pattern or Array of Glob patterns to exclude matches. If a file or a\n * folder matches at least one of the provided patterns, it's not returned.\n * It doesn't prevent files from folder content to be returned. Note: ignore\n * patterns are always in dot:true mode.\n */\n ignore?: string | string[];\n /**\n * Glob pattern or Array of Glob patterns to exclude folders.\n * If a folder matches one of the provided patterns, it's not returned, and\n * it's not explored: this prevents any of its children to be returned.\n * Note: skip patterns are always in dot:true mode.\n */\n skip?: string | string[];\n /**\n * Follow symlinked directories. Note that requires to stat _all_ results,\n * and so reduces performance.\n */\n follow?: boolean;\n /**\n * Set to true to stat _all_ results. This reduces performance.\n */\n stat?: boolean;\n /**\n * Do not match directories, only files.\n */\n nodir?: boolean;\n /**\n * Add a `/` character to directory matches.\n */\n mark?: boolean;\n /**\n * When an unusual error is encountered when attempting to read a directory,\n * a warning will be printed to stderr. Set the `silent` option to true to\n * suppress these warnings.\n */\n silent?: boolean;\n /**\n * Absolute paths will be returned instead of relative paths.\n */\n absolute?: boolean;\n};\n\ntype StrictOptions = Options & Required<\n Omit<Options, 'pattern' | 'ignore' | 'skip'>\n>;\n\n\nfunction readOptions(options: Options) : StrictOptions {\n return {\n pattern: options.pattern,\n dot: !!options.dot,\n noglobstar: !!options.noglobstar,\n matchBase: !!options.matchBase,\n nocase: !!options.nocase,\n ignore: options.ignore,\n skip: options.skip,\n follow: !!options.follow,\n stat: !!options.stat,\n nodir: !!options.nodir,\n mark: !!options.mark,\n silent: !!options.silent,\n absolute: !!options.absolute\n };\n}\n\nexport type Callback = (err: Error | null, matches?: readonly string[]) => void;\n\nexport class ReaddirGlob extends EventEmitter<{\n match: [Match],\n end: [],\n error: [NodeJS.ErrnoException]\n}> {\n public options: StrictOptions;\n\n private matchers: Minimatch[];\n private ignoreMatchers: Minimatch[];\n private skipMatchers: Minimatch[];\n\n public paused: boolean;\n public aborted: boolean;\n private inactive: boolean;\n\n private iterator: ReturnType<typeof explore>;\n\n constructor(cwd?: string, options?: Options | Callback, cb?: Callback) {\n super();\n if(typeof options === 'function') {\n cb = options;\n options = undefined;\n }\n\n this.options = readOptions(options || {});\n \n this.matchers = [];\n if(this.options.pattern) {\n const matchers = Array.isArray(this.options.pattern) ? this.options.pattern : [this.options.pattern];\n this.matchers = matchers.map( m =>\n new Minimatch(m, {\n dot: this.options.dot,\n noglobstar:this.options.noglobstar,\n matchBase:this.options.matchBase,\n nocase:this.options.nocase\n })\n );\n }\n \n this.ignoreMatchers = [];\n if(this.options.ignore) {\n const ignorePatterns = Array.isArray(this.options.ignore) ? this.options.ignore : [this.options.ignore];\n this.ignoreMatchers = ignorePatterns.map( ignore =>\n new Minimatch(ignore, {dot: true})\n );\n }\n \n this.skipMatchers = [];\n if(this.options.skip) {\n const skipPatterns = Array.isArray(this.options.skip) ? this.options.skip : [this.options.skip];\n this.skipMatchers = skipPatterns.map( skip =>\n new Minimatch(skip, {dot: true})\n );\n }\n\n this.iterator = explore(resolve(cwd || '.'), this.options.follow, this.options.stat, this._shouldSkipDirectory.bind(this));\n this.paused = false;\n this.inactive = false;\n this.aborted = false;\n \n if(cb) {\n const nonNullCb = cb;\n const matches: string[] = [];\n this.on('match', match => matches.push(this.options.absolute ? match.absolute : match.relative));\n this.on('error', err => nonNullCb(err));\n this.on('end', () => nonNullCb(null, matches));\n }\n\n setTimeout( () => this._next() );\n }\n\n private _shouldSkipDirectory(relative: string) {\n return this.skipMatchers.some(m => m.match(relative));\n }\n\n private _fileMatches(relative: string, isDirectory: boolean) {\n const file = relative + (isDirectory ? '/' : '');\n return (this.matchers.length === 0 || this.matchers.some(m => m.match(file)))\n && !this.ignoreMatchers.some(m => m.match(file))\n && (!this.options.nodir || !isDirectory);\n }\n\n private _next() {\n if(!this.paused && !this.aborted) {\n this.iterator.next()\n .then((obj)=> {\n if(!obj.done) {\n const isDirectory = obj.value.stat.isDirectory();\n if(this._fileMatches(obj.value.relative, isDirectory )) {\n let relative = obj.value.relative;\n let absolute = obj.value.absolute;\n if(this.options.mark && isDirectory) {\n relative += '/';\n absolute += '/';\n }\n if(this.options.stat) {\n this.emit('match', {relative, absolute, stat:obj.value.stat});\n } else {\n this.emit('match', {relative, absolute});\n }\n }\n this._next();\n } else {\n this.emit('end');\n }\n })\n .catch((err: NodeJS.ErrnoException) => {\n this.abort();\n this.emit('error', err);\n if(!err.code && !this.options.silent) {\n console.error(err);\n }\n });\n } else {\n this.inactive = true;\n }\n }\n\n abort(): void {\n this.aborted = true;\n }\n\n pause(): void {\n this.paused = true;\n }\n\n resume(): void {\n this.paused = false;\n if(this.inactive) {\n this.inactive = false;\n this._next();\n }\n }\n}\n\ninterface readdirGlobInterface {\n (pattern?: string, options?: Options | Callback, cb?: Callback): ReaddirGlob;\n ReaddirGlob: typeof ReaddirGlob;\n}\n\nexport const readdirGlob: readdirGlobInterface = (pattern?: string, options?: Options | Callback, cb?: Callback) =>\n new ReaddirGlob(pattern, options, cb);\n\nreaddirGlob.ReaddirGlob = ReaddirGlob;\n\nexport default readdirGlob;\n"],"mappings":"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"}