diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..1aa724a --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,26 @@ +# Test against the latest versions of these Node.js versions +environment: + matrix: + # node.js + - nodejs_version: "4" + - nodejs_version: "5" + - nodejs_version: "6" + - nodejs_version: "7" + +# Install scripts. (runs after repo cloning) +install: + # Get the latest stable version of Node.js or io.js + - ps: Install-Product node $env:nodejs_version + # install modules + - npm install + +# Post-install test scripts. +test_script: + # Output useful info for debugging. + - node --version + - npm --version + # run tests + - npm test + +# Don't actually build. +build: off \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2259192..cde2fe8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ +.history .DS_Store - +updaters npm-debug.log - -node_modules +node_modules \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index e351614..a3cbb3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: node_js -# test on node.js version 0.10.x node_js: - - 0.10 - + - 8 + - 4 + - 5 + - 0.12 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4dd5598 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# path-reader - Change Log +All notable changes to this project will be documented here. + +## [1.1.0] - 2018-02-14 +- valuetizer option when returns null, item is skipped +- Happy Valentines Day + +## [1.0.6] - 2017-10-13 +- empty folders containing empty items reads are now empty array instead of undefined + +## [1.0.5] - 2017-10-13 +### Fixed +- empty folder reads are now empty array instead of undefined + diff --git a/LICENSE.txt b/LICENSE.txt index 97445a0..5821128 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,5 @@ (The MIT License) -Copyright (c) 2012 Nathan Cartwright - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including diff --git a/README.md b/README.md index 99310a4..8d5d64f 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,39 @@ -[![Build Status](https://secure.travis-ci.org/fshost/node-dir.svg)](http://travis-ci.org/fshost/node-dir) - -# node-dir +[![hire me](https://ackerapple.github.io/resume/assets/images/hire-me-badge.svg)](https://ackerapple.github.io/resume/) +[![npm downloads](https://img.shields.io/npm/dm/path-reader.svg)](https://npmjs.org/path-reader) +[![Dependency Status](https://david-dm.org/ackerapple/path-reader.svg)](https://david-dm.org/ackerapple/path-reader) +[![Build Status](https://secure.travis-ci.org/AckerApple/path-reader.svg)](http://travis-ci.org/AckerApple/path-reader) +[![Build status](https://ci.appveyor.com/api/projects/status/6sa5pfcsrix5s8va?svg=true)](https://ci.appveyor.com/project/AckerApple/path-reader) +[![NPM version](https://img.shields.io/npm/v/path-reader.svg?style=flat-square)](https://www.npmjs.com/package/path-reader) + +# path-reader A lightweight Node.js module with methods for some common directory and file operations, including asynchronous, non-blocking methods for recursively getting an array of files, subdirectories, or both, and methods for recursively, sequentially reading and processing the contents of files in a directory and its subdirectories, with several options available for added flexibility if needed. ### Table of Contents - [installation](#installation) - [usage](#usage) - - [methods](#methods) - - [readFiles( dir, options, fileCallback, finishedCallback)](#readfiles-dir-options-filecallback-finishedcallback) - - [readFilesStream( dir, options, streamCallback, finishedCallback)](#readfilesstream-dir-options-streamcallback-finishedcallback) - - [readFilesStream examples](#readfilesstream-examples) - - [files( dir, callback )](#files-dir-callback) - - [files( dir, {sync:true} )](#files-dir-synctrue) - - [promiseFiles( dir, callback )](#promisefiles-dir-callback) - - [subdirs( dir, callback )](#subdirs-dir-callback) - - [paths(dir, [combine], callback )](#pathsdir-combine-callback) + - [methods](#methods) + - [readFiles](#readfiles) + - [options](#options) + - [readFilesStream](#readfilesstream) + - [readFilesStream examples](#readfilesstream-examples) + - [readFiles valuetizer](#readfiles-valuetizer) + - [files async](#files-async) + - [files sync](#files-sync) + - [promiseFiles](#promisefiles) + - [subdirs](#subdirs) + - [paths](#paths) - [API Docs](#api-docs) - - [files(dir, type, callback, options)](#filesdir-type-callback-options) + - [files](#files-api) + - [promiseFiles](#promisefiles-api) +- [History](#history) - [License](#license) #### installation - npm install node-dir +``` +npm install path-reader +``` ### usage @@ -30,31 +41,59 @@ A lightweight Node.js module with methods for some common directory and file ope For the sake of brevity, assume that the following line of code precedes all of the examples. ```javascript -var dir = require('node-dir'); +var dir = require('path-reader'); +``` + +#### readFiles +A variation on the method readFilesStream. See usage for [readFilesStream](#readFilesStream) +```javascript +readFiles( dir, [options], fileCallback, [finishedCallback] ) ``` -#### readFiles( dir, [options], fileCallback, [finishedCallback] ) -#### readFilesStream( dir, [options], streamCallback, [finishedCallback] ) +#### Options +- **encoding** - file encoding (defaults to 'utf8') +- **exclude** - a regex pattern or array to specify filenames to ignore +- **excludeDir** - a regex pattern or array to specify directories to ignore +- **match** - a regex pattern or array to specify filenames to operate on +- **matchDir** - a regex pattern or array to specify directories to recurse +- **recursive** - whether to recurse subdirectories when reading files (defaults to true) +- **reverse** - sort files in each directory in descending order +- **shortName** - whether to aggregate only the base filename rather than the full filepath +- **sort** - sort files in each directory in ascending order (defaults to true) + - A reverse sort can also be achieved by setting the sort option to 'reverse', 'desc', or 'descending' string value. +- **doneOnErr** - control if done function called on error (defaults to true) +- **sync** : boolean = false - results are returned inline and no callbacks are used +- **shortName** : boolean = false||'relative' - instead of fullpath file names, just get the names or relative item names +- **recursive** : boolean = true - traverse through all children of given path +- **excludeHidden** : boolean - hidden files will be ignored (files starting with a dot are ignored) +- **valuetizer** : (stat, fileName, filePath) - A function of (stat, fileName, fileFullPath) . When null returned, item is skipped in end results + +#### readFilesStream Sequentially read the content of each file in a directory, passing the contents to a callback, optionally calling a finished callback when complete. The options and finishedCallback arguments are not required. -Valid options are: -- encoding: file encoding (defaults to 'utf8') -- exclude: a regex pattern or array to specify filenames to ignore -- excludeDir: a regex pattern or array to specify directories to ignore -- match: a regex pattern or array to specify filenames to operate on -- matchDir: a regex pattern or array to specify directories to recurse -- recursive: whether to recurse subdirectories when reading files (defaults to true) -- reverse: sort files in each directory in descending order -- shortName: whether to aggregate only the base filename rather than the full filepath -- sort: sort files in each directory in ascending order (defaults to true) -- doneOnErr: control if done function called on error (defaults to true) +```javascript +readFilesStream( dir, [options], streamCallback, [finishedCallback] ) +``` -A reverse sort can also be achieved by setting the sort option to 'reverse', 'desc', or 'descending' string value. +#### readFiles valuetizer +An example of building an array of only items with an mtime +```javascript +var options = { + valuetizer:function(stat, shortName, longPath){ + return stat.mtime ? stat : null + } +} + +require('path-reader').promiseFiles(tdir, 'file', options) +.then(function(results){ + console.log(results)//an array of file stat if the file has a mtime definition +}) +``` #### readFilesStream examples +Display contents of files in this script's directory ```javascript -// display contents of files in this script's directory dir.readFiles(__dirname, function(err, content, next) { if (err) throw err; @@ -64,9 +103,12 @@ dir.readFiles(__dirname, function(err, files){ if (err) throw err; console.log('finished reading files:', files); - }); + } +); +``` -// display contents of huge files in this script's directory +Display contents of huge files in this script's directory +```javascript dir.readFilesStream(__dirname, function(err, stream, next) { if (err) throw err; @@ -82,9 +124,12 @@ dir.readFilesStream(__dirname, function(err, files){ if (err) throw err; console.log('finished reading files:', files); - }); + } +); +``` -// match only filenames with a .txt extension and that don't start with a `.´ +Match only filenames with a .txt extension and that don't start with a `.´ +```javascript dir.readFiles(__dirname, { match: /.txt$/, exclude: /^\./ @@ -96,11 +141,14 @@ dir.readFiles(__dirname, { function(err, files){ if (err) throw err; console.log('finished reading files:',files); - }); + } +); +``` -// exclude an array of subdirectory names +Exclude an array of subdirectory names +```javascript dir.readFiles(__dirname, { - exclude: ['node_modules', 'test'] + excludeDir: ['node_modules', 'test'] }, function(err, content, next) { if (err) throw err; console.log('content:', content); @@ -109,19 +157,20 @@ dir.readFiles(__dirname, { function(err, files){ if (err) throw err; console.log('finished reading files:',files); - }); - + } +); +``` -// the callback for each file can optionally have a filename argument as its 3rd parameter -// and the finishedCallback argument is optional, e.g. +The callback for each file can optionally have a filename argument as its 3rd parameter and the finishedCallback argument is optional, e.g. +```javascript dir.readFiles(__dirname, function(err, content, filename, next) { - console.log('processing content of file', filename); - next(); - }); + console.log('processing content of file', filename); + next(); +}); ``` -#### files( dir, callback ) +#### files async Asynchronously iterate the files of a directory and its subdirectories and pass an array of file paths to a callback. ```javascript @@ -131,17 +180,25 @@ dir.files(__dirname, function(err, files) { }); ``` -#### files( dir, {sync:true} ) +#### files sync Synchronously iterate the files of a directory and its subdirectories and pass an array of file paths to a callback. - + +In this example, a console log of items by relative path will be made ```javascript -var files = dir.files(__dirname, {sync:true}); +var files = dir.files(__dirname, {sync:true, shortName:'relative', excludeHidden:true}); console.log(files); ``` -#### promiseFiles( dir, callback ) +#### promiseFiles Asynchronously iterate the files of a directory and its subdirectories and pass an array of file paths to a callback. - + +```javascript +require('path-reader').promiseFiles(path, readType||options, options, fsStatOptions) +``` + +> [read more about fsStatOptions here](https://nodejs.org/api/fs.html#fsstatsyncpath-options) + +promiseFiles example ```javascript dir.promiseFiles(__dirname) .then((files)=>{ @@ -155,14 +212,18 @@ Note that for the files and subdirs the object returned is an array, and thus al ```javascript dir.files(__dirname, function(err, files) { if (err) throw err; + // sort ascending files.sort(); + // sort descending files.reverse(); + // include only certain filenames files = files.filter(function (file) { return ['allowed', 'file', 'names'].indexOf(file) > -1; }); + // exclude some filenames files = files.filter(function (file) { return ['exclude', 'these', 'files'].indexOf(file) === -1; @@ -172,9 +233,14 @@ dir.files(__dirname, function(err, files) { Also note that if you need to work with the contents of the files asynchronously, please use the readFiles method. The files and subdirs methods are for getting a list of the files or subdirs in a directory as an array. -#### subdirs( dir, callback ) +#### subdirs Asynchronously iterate the subdirectories of a directory and its subdirectories and pass an array of directory paths to a callback. +```javascript +subdirs( dir, callback ) +``` + +Example ```javascript dir.subdirs(__dirname, function(err, subdirs) { if (err) throw err; @@ -182,11 +248,14 @@ dir.subdirs(__dirname, function(err, subdirs) { }); ``` -#### paths(dir, [combine], callback ) +#### paths Asynchronously iterate the subdirectories of a directory and its subdirectories and pass an array of both file and directory paths to a callback. -Separated into two distinct arrays (paths.files and paths.dirs) +```javascript +paths(dir, [combine], callback ) +``` +Example: Separated into two distinct arrays (paths.files and paths.dirs) ```javascript dir.paths(__dirname, function(err, paths) { if (err) throw err; @@ -207,7 +276,31 @@ dir.paths(__dirname, true, function(err, paths) { ## API Docs -### files(dir, type, callback, options) +### files API + +```javascript +files(dir, type, callback, options) +``` + +- **dir** - directory path to read +- **type**='file' + - 'file' returns only file listings + - 'dir' returns only directory listings + - 'all' returns {dirs:[], files:[]} + - 'combine' returns [] +- **callback** - +- **options** + - **sync**=false - results are returned inline and no callbacks are used + - **shortName**=false||'relative' - instead of fullpath file names, just get the names or relative item names + - **recursive**=true - traverse through all children of given path + - **excludeHidden** - hidden files will be ignored (files starting with a dot are ignored) + - **valuetizer** - A function of (stat, fileShortName, fileFullPath) . When null returned, item is skipped in end results + +### promiseFiles API + +```javascript +promiseFiles(dir, type||options, options) +``` - **dir** - directory path to read - **type**='file' @@ -215,11 +308,14 @@ dir.paths(__dirname, true, function(err, paths) { - 'dir' returns only directory listings - 'all' returns {dirs:[], files:[]} - 'combine' returns [] -- **callback** - - **options** - - **sync**=false - results are returned inline and no callbacks are used - - **shortName**=false - instead of fullpath file names, just get the names + - **sync**=false - DO NOT USE for promiseFiles, will cause unexpected behavior + - **shortName**=false||'relative' - instead of fullpath file names, just get the names or relative item names - **recursive**=true - traverse through all children of given path + - **valuetizer** - A function of (stat, fileShortName, fileFullPath) . When null returned, item is skipped in end results + +## History +path-reader is a fork of node-dir. The original maintainer of node-dir, @fshost, has not updated nor been heard from in some time. Use path-reader, it is far superior to node-dir. ## License MIT licensed (See LICENSE.txt) diff --git a/index.js b/index.js deleted file mode 100644 index 49e3555..0000000 --- a/index.js +++ /dev/null @@ -1,5 +0,0 @@ -var dirpaths = require('./lib/paths'); - -Object.assign(exports, dirpaths) -exports.readFiles = require('./lib/readfiles'); -exports.readFilesStream = require('./lib/readfilesstream'); diff --git a/lib/index.d.ts b/lib/index.d.ts new file mode 100644 index 0000000..3196806 --- /dev/null +++ b/lib/index.d.ts @@ -0,0 +1,3 @@ +export * from './paths'; +export { readFiles } from './readfiles'; +export { readFilesStream } from './readfilesstream'; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..8468329 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,22 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.readFilesStream = exports.readFiles = void 0; +__exportStar(require("./paths"), exports); +var readfiles_1 = require("./readfiles"); +Object.defineProperty(exports, "readFiles", { enumerable: true, get: function () { return readfiles_1.readFiles; } }); +var readfilesstream_1 = require("./readfilesstream"); +Object.defineProperty(exports, "readFilesStream", { enumerable: true, get: function () { return readfilesstream_1.readFilesStream; } }); diff --git a/lib/paths.d.ts b/lib/paths.d.ts new file mode 100644 index 0000000..ac3d011 --- /dev/null +++ b/lib/paths.d.ts @@ -0,0 +1,13 @@ +/// +import * as fs from 'fs'; +interface FileReadOptions { + basePath?: string; + recursive?: boolean; + excludeHidden?: boolean; + sync?: boolean; + ignoreType?: string; + valuetizer?: (stat: any, shortName: string, longPath: string, isDir?: boolean) => any; +} +export declare function promiseFiles(dir: any, type: any, options?: FileReadOptions, statOptions?: fs.StatSyncOptions): Promise; +export declare function files(dir: any, type: any, callback: any, options?: FileReadOptions, statOptions?: fs.StatSyncOptions): any; +export {}; diff --git a/lib/paths.js b/lib/paths.js index 476358e..fdce7fb 100644 --- a/lib/paths.js +++ b/lib/paths.js @@ -1,240 +1,242 @@ -var fs = require('fs'), - path = require('path'); - -exports.promiseFiles = function promiseFiles(dir, type, options){ - type = type || 'file' - - var processor = function(res,rej){ - var cb = function(err,data){ - if(err)return rej(err) - res(data) +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.files = exports.promiseFiles = void 0; +var fs = require("fs"); +var path = require("path"); +function promiseFiles(dir, type, options, statOptions) { + switch (typeof type) { + case 'object': + options = type; + type = 'file'; + break; + default: type = type || 'file'; } - exports.files(dir,type,cb,options) - } - return new Promise(processor) + var processor = function (res, rej) { + var cb = function (err, data) { + if (err) + return rej(err); + res(data); + }; + exports.files(dir, type, cb, options, statOptions); + }; + return new Promise(processor); } - -/** - * find all files or subdirs (recursive) and pass to callback fn - * - * @param {string} dir directory in which to recurse files or subdirs - * @param {string} type type of dir entry to recurse ('file', 'dir', or 'all', defaults to 'file') - * @param {function(error, )} callback fn to call when done - * @example - * dir.files(__dirname, function(err, files) { - * if (err) throw err; - * console.log('files:', files); - * }); - */ -exports.files = function files(dir, type, callback, options) { - var ofType = typeof type - if(ofType == 'object'){ - options = options || type - type = 'file' - callback = function(){} - }else if (ofType !== 'string') { - //ignoreType = callback; - callback = type; - type = 'file'; - } - - options = options || {} - - var pending, - results = { - files: [], - dirs: [] - }; - - var done = function() { - if(type==='combine'){ - results = results.files.concat(results.dirs) - } else if (!type || options.ignoreType || ['all','combine'].indexOf(type)>=0) { - results = results - } else { - results = results[type + 's'] +exports.promiseFiles = promiseFiles; +function files(dir, type, callback, options, statOptions) { + var ofType = typeof type; + if (ofType == 'object') { + options = options || type; + type = 'file'; + callback = function () { }; } - - if(options.sync)return; - - - callback(null, results); - }; - - var getStatHandler = function(statPath, name, lstatCalled) { - return function(err, stat) { - if (err) { - if (!lstatCalled) { - return fs.lstat(statPath, getStatHandler(statPath, name, true)); + else if (ofType !== 'string') { + callback = type; + type = 'file'; + } + options = options || {}; + var pending; + var results = { + files: [], + dirs: [] + }; + var done = function (_list) { + if (type === 'combine') { + results = results.files.concat(results.dirs); + } + else if (!type || options.ignoreType || ['all', 'combine'].indexOf(type) >= 0) { + results = results; } - return callback(err); - } - - var pushVal = options.shortName ? name : statPath - - if (stat && stat.isDirectory() && stat.mode !== 17115) { - if (type !== 'file') { - results.dirs.push(pushVal); + else { + results = results[type + 's']; } - - if (options.recursive==null || options.recursive) { - var subloop = function(err, res) { - if (err){ - return callback(err) + if (options.sync) + return; + callback(null, results); + }; + var getStatHandler = function (statPath, name, statHanOptions) { + return function (err, stat) { + if (err) { + if (!statHanOptions.lstatCalled) { + var newStatHanOptions = assign(statHanOptions, { lstatCalled: true }); + if (options.sync) { + var lstat = fs.lstatSync(statPath); + return getStatHandler(statPath, name, newStatHanOptions)(null, lstat); + } + else { + return fs.lstat(statPath, getStatHandler(statPath, name, newStatHanOptions)); + } + } + return callback(err); + } + var isDir = stat && stat.isDirectory() && stat.mode !== 17115; + var pushVal = statHanOptions.valuetizer(stat, name, statPath, isDir); + if (pushVal == null) { + if (!--pending) { + done(); + } + return; } - - if(type === 'combine'){ - results.files = results.files.concat(res); - }else if (type === 'all') { - results.files = results.files.concat(res.files); - results.dirs = results.dirs.concat(res.dirs); - } else if (type === 'file') { - results.files = results.files.concat(res.files); - } else { - results.dirs = results.dirs.concat(res.dirs); + if (isDir) { + if (type !== 'file') { + results.dirs.push(pushVal); + } + if (options.recursive == null || options.recursive) { + var subloop = function (err, res) { + if (err) { + return callback(err); + } + if (type === 'combine') { + results.files = results.files.concat(res); + } + else if (type === 'all') { + if (res.files) { + results.files = results.files.concat(res.files); + } + results.dirs = results.dirs.concat(res.dirs); + } + else if (type === 'file') { + if (res.files) { + results.files = results.files.concat(res.files); + } + } + else { + results.dirs = results.dirs.concat(res.dirs); + } + if (!--pending) { + done(); + } + }; + var newOptions = assign({}, options); + newOptions.basePath = options.basePath || dir; + newOptions.ignoreType = true; + var moreResults = files(statPath, type, subloop, newOptions); + if (options.sync) { + subloop(null, moreResults); + } + } + else if (!--pending) { + done(); + } } - - if (!--pending){ - done(); + else { + var excludeHidden = options.excludeHidden && name.split(path.sep).pop().search(/^\./) == 0; + if (type !== 'dir' && !excludeHidden) { + results.files.push(pushVal); + } + if (!--pending) { + done(); + } } - } - - var newOptions = Object.assign({}, options) - newOptions.ignoreType = true - var moreResults = files(statPath, type, subloop, newOptions); - - if(options.sync){ - subloop(null, moreResults) - } - }else if (!--pending){ - done() + }; + }; + var onDirRead = function (err, list, statOptions) { + if (err) + return callback(err); + pending = list.length; + if (!pending) { + done(list); + return list; } - } else { - if (type !== 'dir') { - results.files.push(pushVal); + var statHanOptions = {}; + if (options.valuetizer) { + statHanOptions.valuetizer = options.valuetizer; } - // should be the last statement in statHandler - if (!--pending){ - done() + else { + statHanOptions.valuetizer = getValuetizerByOptions(options, dir); } - } - } - } - - var bufdir = Buffer.from(dir); - - const onDirRead = function(err, list) { - if (err) return callback(err); - - pending = list.length; - if (!pending) return done(); - - for (var file, i = 0, l = list.length; i < l; i++) { - var fname = list[i].toString(); - file = path.join(dir, fname); - var buffile = Buffer.concat([bufdir, Buffer.from(path.sep), list[i]]); - - if(options.sync){ - var res = fs.statSync(buffile); - getStatHandler(file,fname)(null, res) - }else{ - fs.stat(buffile, getStatHandler(file,fname)); - } + for (var file, i = 0, l = list.length; i < l; i++) { + var fname = list[i].toString(); + file = path.join(dir, fname); + if (options.sync) { + var res = fs.statSync(file, statOptions); + getStatHandler(file, list[i], statHanOptions)(null, res); + } + else { + fs.stat(file, getStatHandler(file, list[i], statHanOptions)); + } + } + return results; + }; + var onStat = function (err, stat, statOptions) { + if (err) + return callback(err); + if (stat && stat.mode === 17115) + return done(); + if (options.sync) { + var list = fs.readdirSync(dir); + return onDirRead(null, list, statOptions); + } + else { + fs.readdir(dir, function (err, files) { return onDirRead(err, files, statOptions); }); + } + }; + if (options.sync) { + var stat = fs.statSync(dir); + return onStat(null, stat, statOptions); } - - return results - } - - const onStat = function(err, stat) { - if (err) return callback(err); - if (stat && stat.mode === 17115) return done(); - - if(options.sync){ - const list = fs.readdirSync(bufdir, {encoding: 'buffer'}) - return onDirRead(null, list) - }else{ - fs.readdir(bufdir, {encoding: 'buffer'}, onDirRead) + else { + fs.stat(dir, onStat); } - } - - if(options.sync){ - const stat = fs.statSync(bufdir); - return onStat(null, stat) - }else{ - fs.stat(bufdir, onStat); - } -}; - - -/** - * find all files and subdirs in a directory (recursive) and pass them to callback fn - * - * @param {string} dir directory in which to recurse files or subdirs - * @param {boolean} combine whether to combine both subdirs and filepaths into one array (default false) - * @param {function(error, Object.<, Array.>)} callback fn to call when done - * @example - * dir.paths(__dirname, function (err, paths) { - * if (err) throw err; - * console.log('files:', paths.files); - * console.log('subdirs:', paths.dirs); - * }); - * dir.paths(__dirname, true, function (err, paths) { - * if (err) throw err; - * console.log('paths:', paths); - * }); - */ +} +exports.files = files; +; exports.paths = function paths(dir, combine, callback) { - - var type; - if (typeof combine === 'function') { callback = combine; combine = false; } - - exports.files(dir, 'all', function(err, results) { - if (err) return callback(err); + files(dir, 'all', function (err, results) { + if (err) + return callback(err); if (combine) { - callback(null, results.files.concat(results.dirs)); - } else { + } + else { callback(null, results); } }); }; - - -/** - * find all subdirs (recursive) of a directory and pass them to callback fn - * - * @param {string} dir directory in which to find subdirs - * @param {string} type type of dir entry to recurse ('file' or 'dir', defaults to 'file') - * @param {function(error, )} callback fn to call when done - * @example - * dir.subdirs(__dirname, function (err, paths) { - * if (err) throw err; - * console.log('files:', paths.files); - * console.log('subdirs:', paths.dirs); - * }); - */ exports.subdirs = function subdirs(dir, callback, type, options) { - options = options || {} - - const iCallback = function(err, subdirs) { - if (err) return callback(err); - - if(type=='combine'){ - subdirs = subdirs.files.concat(subdirs.dirs) + options = options || {}; + var iCallback = function (err, subdirs) { + if (err) + return callback(err); + if (type == 'combine') { + subdirs = subdirs.files.concat(subdirs.dirs); + } + if (options.sync) + return subdirs; + callback(null, subdirs); + }; + var res = exports.files(dir, 'dir', iCallback, options); + if (options && options.sync) { + return iCallback(null, res); } - - if(options.sync)return subdirs - - callback(null, subdirs); - } - - const res = exports.files(dir, 'dir', iCallback, options) - - if(options && options.sync){ - return iCallback(null,res) - } }; +function assign(c0, c1) { + for (var x in c1) + c0[x] = c1[x]; + return c0; +} +function getValuetizerByOptions(options, dir) { + if (options.shortName) { + if (options.shortName == 'relative') { + var dirBase = (options.basePath || dir); + var startPos = dirBase.length; + if (dirBase.substring(dirBase.length - path.sep.length, dirBase.length) != path.sep) { + startPos = startPos + path.sep.length; + } + return function (stat, shortName, longPath, isDir) { + return longPath.substring(startPos, longPath.length); + }; + } + else { + return function (stat, shortName, longPath) { + return shortName; + }; + } + } + return function (stat, shortName, longPath) { + return longPath; + }; +} diff --git a/lib/readfiles.d.ts b/lib/readfiles.d.ts new file mode 100644 index 0000000..5e4cbc9 --- /dev/null +++ b/lib/readfiles.d.ts @@ -0,0 +1 @@ +export declare function readFiles(dir: any, options: any, callback: any, complete: any): void; diff --git a/lib/readfiles.js b/lib/readfiles.js index ea3df03..e08f20b 100644 --- a/lib/readfiles.js +++ b/lib/readfiles.js @@ -1,16 +1,12 @@ -var fs = require('fs'), - path = require('path'); - -/** - * merge two objects by extending target object with source object - * @param target object to merge - * @param source object to merge - * @param {Boolean} [modify] whether to modify the target - * @returns {Object} extended object - */ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.readFiles = void 0; +var fs = require("fs"); +var path = require("path"); function extend(target, source, modify) { var result = target ? modify ? target : extend({}, target, true) : {}; - if (!source) return result; + if (!source) + return result; for (var key in source) { if (source.hasOwnProperty(key) && source[key] !== undefined) { result[key] = source[key]; @@ -18,97 +14,106 @@ function extend(target, source, modify) { } return result; } - -/** - * determine if a string is contained within an array or matches a regular expression - * @param {String} str string to match - * @param {Array|Regex} match array or regular expression to match against - * @returns {Boolean} whether there is a match - */ function matches(str, match) { - if (Array.isArray(match)) return match.indexOf(str) > -1; + if (Array.isArray(match)) + return match.indexOf(str) > -1; return match.test(str); } - -/** - * read files and call a function with the contents of each file - * @param {String} dir path of dir containing the files to be read - * @param {String} encoding file encoding (default is 'utf8') - * @param {Object} options options hash for encoding, recursive, and match/exclude - * @param {Function(error, string)} callback callback for each files content - * @param {Function(error)} complete fn to call when finished - */ function readFiles(dir, options, callback, complete) { if (typeof options === 'function') { complete = callback; callback = options; options = {}; } - if (typeof options === 'string') options = { - encoding: options - }; + if (typeof options === 'string') + options = { + encoding: options + }; options = extend({ recursive: true, encoding: 'utf8', doneOnErr: true }, options); var files = []; - - var done = function(err) { + var done = function (err, _result) { if (typeof complete === 'function') { - if (err) return complete(err); + if (err) + return complete(err); complete(null, files); } }; - - fs.readdir(dir, function(err, list) { - if (err)  { + fs.readdir(dir, function (err, list) { + if (err) { if (options.doneOnErr === true) { - if (err.code === 'EACCES') return done(); - return done(err); + if (err.code === 'EACCES') + return done(); + return done(err); } } var i = 0; - if (options.reverse === true || (typeof options.sort == 'string' && (/reverse|desc/i).test(options.sort))) { list = list.reverse(); - } else if (options.sort !== false) list = list.sort(); - + } + else if (options.sort !== false) + list = list.sort(); (function next() { var filename = list[i++]; - if (!filename) return done(null, files); + if (!filename) + return done(null, files); var file = path.join(dir, filename); - fs.stat(file, function(err, stat) { - if (err && options.doneOnErr === true) return done(err); + fs.stat(file, function (err, stat) { + if (err && options.doneOnErr === true) + return done(err); if (stat && stat.isDirectory()) { if (options.recursive) { - if (options.matchDir && !matches(filename, options.matchDir)) return next(); - if (options.excludeDir && matches(filename, options.excludeDir)) return next(); - readFiles(file, options, callback, function(err, sfiles) { - if (err && options.doneOnErr === true) return done(err); + if (options.matchDir && !matches(filename, options.matchDir)) + return next(); + if (options.excludeDir && matches(filename, options.excludeDir)) + return next(); + readFiles(file, options, callback, function (err, sfiles) { + if (err && options.doneOnErr === true) + return done(err); files = files.concat(sfiles); next(); }); - } else next(); - } else if (stat && stat.isFile()) { - if (options.match && !matches(filename, options.match)) return next(); - if (options.exclude && matches(filename, options.exclude)) return next(); - if (options.filter && !options.filter(filename)) return next(); - if (options.shortName) files.push(filename); - else files.push(file); - fs.readFile(file, options.encoding, function(err, data) { + } + else + next(); + } + else if (stat && stat.isFile()) { + if (options.match && !matches(filename, options.match)) + return next(); + if (options.exclude && matches(filename, options.exclude)) + return next(); + if (options.filter && !options.filter(filename)) + return next(); + if (options.shortName) { + files.push(filename); + } + else { + files.push(file); + } + fs.readFile(file, options.encoding, function (err, data) { if (err) { - if (err.code === 'EACCES') return next(); + if (err.code === 'EACCES') + return next(); if (options.doneOnErr === true) { return done(err); } } - if (callback.length > 3) - if (options.shortName) callback(null, data, filename, next); - else callback(null, data, file, next); - else callback(null, data, next); + if (callback.length > 3) { + if (options.shortName) { + callback(null, data, filename, next); + } + else { + callback(null, data, file, next); + } + } + else { + callback(null, data, next); + } }); } else { @@ -116,7 +121,6 @@ function readFiles(dir, options, callback, complete) { } }); })(); - }); } -module.exports = readFiles; +exports.readFiles = readFiles; diff --git a/lib/readfilesstream.d.ts b/lib/readfilesstream.d.ts new file mode 100644 index 0000000..d8a6a9f --- /dev/null +++ b/lib/readfilesstream.d.ts @@ -0,0 +1 @@ +export declare function readFilesStream(dir: any, options: any, callback: any, complete: any): void; diff --git a/lib/readfilesstream.js b/lib/readfilesstream.js index b318ccf..ed65017 100644 --- a/lib/readfilesstream.js +++ b/lib/readfilesstream.js @@ -1,17 +1,12 @@ -var fs = require('fs'), - mm = require('minimatch'), - path = require('path'); - -/** - * merge two objects by extending target object with source object - * @param target object to merge - * @param source object to merge - * @param {Boolean} [modify] whether to modify the target - * @returns {Object} extended object - */ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.readFilesStream = void 0; +var fs = require('fs'), mm = require('minimatch'); +var path = require("path"); function extend(target, source, modify) { var result = target ? modify ? target : extend({}, target, true) : {}; - if (!source) return result; + if (!source) + return result; for (var key in source) { if (source.hasOwnProperty(key) && source[key] !== undefined) { result[key] = source[key]; @@ -19,18 +14,11 @@ function extend(target, source, modify) { } return result; } - -/** - * determine if a string is contained within an array or matches a regular expression - * @param {String} str string to match - * @param {Array|Regex} match array or regular expression to match against - * @returns {Boolean} whether there is a match - */ function matches(str, match) { if (Array.isArray(match)) { var l = match.length; - for( var s=0; s < l; s++) { - if ( mm(str,match[s])) { + for (var s = 0; s < l; s++) { + if (mm(str, match[s])) { return true; } } @@ -38,94 +26,102 @@ function matches(str, match) { } return match.test(str); } - -/** - * read files and call a function with the contents of each file - * @param {String} dir path of dir containing the files to be read - * @param {String} encoding file encoding (default is 'utf8') - * @param {Object} options options hash for encoding, recursive, and match/exclude - * @param {Function(error, string)} callback callback for each files content - * @param {Function(error)} complete fn to call when finished - */ function readFilesStream(dir, options, callback, complete) { if (typeof options === 'function') { complete = callback; callback = options; options = {}; } - if (typeof options === 'string') options = { - encoding: options - }; + if (typeof options === 'string') + options = { + encoding: options + }; options = extend({ recursive: true, encoding: 'utf8', doneOnErr: true }, options); var files = []; - - var done = function(err) { + var done = function (err, _result) { if (typeof complete === 'function') { - if (err) return complete(err); + if (err) + return complete(err); complete(null, files); } }; - - fs.readdir(dir, function(err, list) { - if (err)  { + fs.readdir(dir, function (err, list) { + if (err) { if (options.doneOnErr === true) { - if (err.code === 'EACCES') return done(); - return done(err); + if (err.code === 'EACCES') + return done(); + return done(err); } } var i = 0; - if (options.reverse === true || (typeof options.sort == 'string' && (/reverse|desc/i).test(options.sort))) { list = list.reverse(); - } else if (options.sort !== false) list = list.sort(); - + } + else if (options.sort !== false) + list = list.sort(); (function next() { var filename = list[i++]; - if (!filename) return done(null, files); + if (!filename) + return done(null, files); var file = path.join(dir, filename); - fs.stat(file, function(err, stat) { - if (err && options.doneOnErr === true) return done(err); + fs.stat(file, function (err, stat) { + if (err && options.doneOnErr === true) + return done(err); if (stat && stat.isDirectory()) { if (options.recursive) { - if (options.matchDir && !matches(filename, options.matchDir)) return next(); - if (options.excludeDir && matches(filename, options.excludeDir)) return next(); - readFilesStream(file, options, callback, function(err, sfiles) { - if (err && options.doneOnErr === true) return done(err); + if (options.matchDir && !matches(filename, options.matchDir)) + return next(); + if (options.excludeDir && matches(filename, options.excludeDir)) + return next(); + readFilesStream(file, options, callback, function (err, sfiles) { + if (err && options.doneOnErr === true) + return done(err); files = files.concat(sfiles); next(); }); - } else next(); - } else if (stat && stat.isFile()) { - if (options.match && !matches(filename, options.match)) return next(); - if (options.exclude && matches(filename, options.exclude)) return next(); - if (options.filter && !options.filter(filename)) return next(); - if (options.shortName) files.push(filename); - else files.push(file); + } + else + next(); + } + else if (stat && stat.isFile()) { + if (options.match && !matches(filename, options.match)) + return next(); + if (options.exclude && matches(filename, options.exclude)) + return next(); + if (options.filter && !options.filter(filename)) + return next(); + if (options.shortName) + files.push(filename); + else + files.push(file); var stream = fs.createReadStream(file); if (options.encoding !== null) { stream.setEncoding(options.encoding); } - stream.on('error',function(err) { - if (options.doneOnErr === true) return done(err); - next(); + stream.on('error', function (err) { + if (options.doneOnErr === true) + return done(err); + next(); }); if (callback.length > 3) - if (options.shortName) callback(null, stream, filename, next); - else callback(null, stream, file, next); - else callback(null, stream, next); + if (options.shortName) + callback(null, stream, filename, next); + else + callback(null, stream, file, next); + else + callback(null, stream, next); } else { - next(); + next(); } }); })(); - }); } -module.exports = readFilesStream; +exports.readFilesStream = readFilesStream; diff --git a/lib_old/paths.js b/lib_old/paths.js new file mode 100644 index 0000000..66906a9 --- /dev/null +++ b/lib_old/paths.js @@ -0,0 +1,336 @@ +var fs = require('fs'), + path = require('path'); + +exports.promiseFiles = function promiseFiles(dir, type, options){ + switch(typeof type){ + case 'object': + options = type + type = 'file' + break; + + default:type = type || 'file' + } + + var processor = function(res,rej){ + var cb = function(err,data){ + if(err)return rej(err) + res(data) + } + exports.files(dir,type,cb,options) + } + return new Promise(processor) +} + +/** + * find all files or subdirs (recursive) and pass to callback fn + * + * @param {string} dir directory in which to recurse files or subdirs + * @param {string} type type of dir entry to recurse ('file', 'dir', or 'all', defaults to 'file') + * @param {function(error, )} callback fn to call when done + * @example + * dir.files(__dirname, function(err, files) { + * if (err) throw err; + * console.log('files:', files); + * }); + */ +exports.files = function files(dir, type, callback, options) { + var ofType = typeof type + if(ofType == 'object'){ + options = options || type + type = 'file' + callback = function(){} + }else if (ofType !== 'string') { + //ignoreType = callback; + callback = type; + type = 'file'; + } + + options = options || {} + + var pending + var results = { + files: [], + dirs: [] + }; + + var done = function() { + if(type==='combine'){ + results = results.files.concat(results.dirs) + } else if (!type || options.ignoreType || ['all','combine'].indexOf(type)>=0) { + results = results + } else { + results = results[type + 's'] + } + + if(options.sync)return; + + callback(null, results); + }; + + /** + @statPath - fullPath + @name - fileName + @statHanOptions - { + valuetizer:function(stat, shortName, longPath){}, - function the handles value assignment + lstatCalled:false - used internally + } + */ + var getStatHandler = function(statPath, name, statHanOptions) { + return function(err, stat) { + if (err) { + if (!statHanOptions.lstatCalled) { + var newStatHanOptions = assign(statHanOptions, {lstatCalled:true}) + if(options.sync){ + var lstat = fs.lstatSync(statPath); + return getStatHandler(statPath, name, newStatHanOptions)(null, lstat) + }else{ + return fs.lstat(statPath, getStatHandler(statPath, name, newStatHanOptions)); + } + } + return callback(err); + } + + var isDir = stat && stat.isDirectory() && stat.mode !== 17115 + var pushVal = statHanOptions.valuetizer(stat, name, statPath, isDir)//options.shortName ? name : statPath + + if( pushVal==null ){ + if (!--pending){ + done(); + } + return + } + + if (isDir) { + if (type !== 'file') { + results.dirs.push(pushVal); + } + + if (options.recursive==null || options.recursive) { + var subloop = function(err, res) { + if (err){ + return callback(err) + } + + if(type === 'combine'){ + results.files = results.files.concat(res); + }else if (type === 'all') { + if( res.files ){//dont add things that are not there + results.files = results.files.concat(res.files); + } + results.dirs = results.dirs.concat(res.dirs); + } else if (type === 'file') { + if( res.files ){//dont add things that are not there + results.files = results.files.concat(res.files); + } + } else { + results.dirs = results.dirs.concat(res.dirs); + } + + if (!--pending){ + done(); + } + } + + var newOptions = assign({}, options) + newOptions.basePath = options.basePath || dir + newOptions.ignoreType = true + var moreResults = exports.files(statPath, type, subloop, newOptions); + + if(options.sync){ + subloop(null, moreResults) + } + }else if (!--pending){ + done() + } + } else { + var excludeHidden = options.excludeHidden && name.split(path.sep).pop().search(/^\./)==0 + + if (type!=='dir' && !excludeHidden) { + results.files.push(pushVal); + } + // should be the last statement in statHandler + if (!--pending){ + done() + } + } + } + } + + + //var bufdir = Buffer.from(dir); + + const onDirRead = function(err, list) { + if (err) return callback(err); + + pending = list.length; + if (!pending){ + done(list); + return list + } + + var statHanOptions = {} + if( options.valuetizer ){ + statHanOptions.valuetizer = options.valuetizer + }else{ + statHanOptions.valuetizer = getValuetizerByOptions(options, dir) + } +/* + var statHanOptions = {} + if(options.shortName){ + if(options.shortName=='relative'){ + var dirBase = (options.basePath||dir) + var startPos = dirBase.length + if(dirBase.substring(dirBase.length-path.sep.length, dirBase.length)!=path.sep){ + startPos = startPos + path.sep.length + } + + statHanOptions.valuetizer = function(stat, shortName, longPath, isDir){ + return longPath.substring(startPos, longPath.length) + } + }else{ + statHanOptions.valuetizer = function(stat, shortName, longPath){ + return shortName + } + } + }else{ + statHanOptions.valuetizer = function(stat, shortName, longPath){ + return longPath + } + } +*/ + for (var file, i = 0, l = list.length; i < l; i++) { + var fname = list[i].toString(); + file = path.join(dir, fname); + //var buffile = Buffer.concat([bufdir, Buffer.from(path.sep), list[i]]); + + if(options.sync){ + var res = fs.statSync(file); + getStatHandler(file, list[i], statHanOptions)(null, res) + }else{ + fs.stat(file, getStatHandler(file, list[i], statHanOptions)); + } + } + + return results + } + + const onStat = function(err, stat) { + if (err) return callback(err); + if (stat && stat.mode === 17115) return done(); + + if(options.sync){ + const list = fs.readdirSync(dir) + return onDirRead(null, list) + }else{ + fs.readdir(dir, onDirRead) + } + } + + if(options.sync){ + const stat = fs.statSync(dir); + return onStat(null, stat) + }else{ + fs.stat(dir, onStat); + } +}; + + +/** + * find all files and subdirs in a directory (recursive) and pass them to callback fn + * + * @param {string} dir directory in which to recurse files or subdirs + * @param {boolean} combine whether to combine both subdirs and filepaths into one array (default false) + * @param {function(error, Object.<, Array.>)} callback fn to call when done + * @example + * dir.paths(__dirname, function (err, paths) { + * if (err) throw err; + * console.log('files:', paths.files); + * console.log('subdirs:', paths.dirs); + * }); + * dir.paths(__dirname, true, function (err, paths) { + * if (err) throw err; + * console.log('paths:', paths); + * }); + */ +exports.paths = function paths(dir, combine, callback) { + var type; + + if (typeof combine === 'function') { + callback = combine; + combine = false; + } + + exports.files(dir, 'all', function(err, results) { + if (err) return callback(err); + if (combine) { + callback(null, results.files.concat(results.dirs)); + } else { + callback(null, results); + } + }); +}; + + +/** + * find all subdirs (recursive) of a directory and pass them to callback fn + * + * @param {string} dir directory in which to find subdirs + * @param {string} type type of dir entry to recurse ('file' or 'dir', defaults to 'file') + * @param {function(error, )} callback fn to call when done + * @example + * dir.subdirs(__dirname, function (err, paths) { + * if (err) throw err; + * console.log('files:', paths.files); + * console.log('subdirs:', paths.dirs); + * }); + */ +exports.subdirs = function subdirs(dir, callback, type, options) { + options = options || {} + + const iCallback = function(err, subdirs) { + if (err) return callback(err); + + if(type=='combine'){ + subdirs = subdirs.files.concat(subdirs.dirs) + } + + if(options.sync)return subdirs + + callback(null, subdirs); + } + + const res = exports.files(dir, 'dir', iCallback, options) + + if(options && options.sync){ + return iCallback(null,res) + } +} + +function assign(c0, c1){ + for(var x in c1)c0[x] = c1[x] + return c0 +} + +function getValuetizerByOptions(options, dir){ + if(options.shortName){ + if( options.shortName=='relative' ){ + var dirBase = (options.basePath||dir) + var startPos = dirBase.length + if(dirBase.substring(dirBase.length-path.sep.length, dirBase.length)!=path.sep){ + startPos = startPos + path.sep.length + } + + return function(stat, shortName, longPath, isDir){ + return longPath.substring(startPos, longPath.length) + } + }else{ + return function(stat, shortName, longPath){ + return shortName + } + } + } + + return function(stat, shortName, longPath){ + return longPath + } +} diff --git a/lib_old/readfiles.js b/lib_old/readfiles.js new file mode 100644 index 0000000..c45ae1b --- /dev/null +++ b/lib_old/readfiles.js @@ -0,0 +1,134 @@ +var fs = require('fs'), + path = require('path'); + +/** + * merge two objects by extending target object with source object + * @param target object to merge + * @param source object to merge + * @param {Boolean} [modify] whether to modify the target + * @returns {Object} extended object + */ +function extend(target, source, modify) { + var result = target ? modify ? target : extend({}, target, true) : {}; + if (!source) return result; + for (var key in source) { + if (source.hasOwnProperty(key) && source[key] !== undefined) { + result[key] = source[key]; + } + } + return result; +} + +/** + * determine if a string is contained within an array or matches a regular expression + * @param {String} str string to match + * @param {Array|Regex} match array or regular expression to match against + * @returns {Boolean} whether there is a match + */ +function matches(str, match) { + if (Array.isArray(match)) return match.indexOf(str) > -1; + return match.test(str); +} + +/** + * read files and call a function with the contents of each file + * @param {String} dir path of dir containing the files to be read + * @param {String} encoding file encoding (default is 'utf8') + * @param {Object} options options hash for encoding, recursive, and match/exclude + * @param {Function(error, string)} callback callback for each files content + * @param {Function(error)} complete fn to call when finished + */ +function readFiles(dir, options, callback, complete) { + if (typeof options === 'function') { + complete = callback; + callback = options; + options = {}; + } + if (typeof options === 'string') options = { + encoding: options + }; + options = extend({ + recursive: true, + encoding: 'utf8', + doneOnErr: true + }, options); + var files = []; + + var done = function(err) { + if (typeof complete === 'function') { + if (err) return complete(err); + complete(null, files); + } + }; + + fs.readdir(dir, function(err, list) { + if (err) { + if (options.doneOnErr === true) { + if (err.code === 'EACCES') return done(); + return done(err); + } + } + var i = 0; + + if (options.reverse === true || + (typeof options.sort == 'string' && + (/reverse|desc/i).test(options.sort))) { + list = list.reverse(); + } else if (options.sort !== false) list = list.sort(); + + (function next() { + var filename = list[i++]; + if (!filename) return done(null, files); + + var file = path.join(dir, filename); + + fs.stat(file, function(err, stat) { + if (err && options.doneOnErr === true) return done(err); + if (stat && stat.isDirectory()) { + if (options.recursive) { + if (options.matchDir && !matches(filename, options.matchDir)) return next(); + if (options.excludeDir && matches(filename, options.excludeDir)) return next(); + readFiles(file, options, callback, function(err, sfiles) { + if (err && options.doneOnErr === true) return done(err); + files = files.concat(sfiles); + next(); + }); + } else next(); + } else if (stat && stat.isFile()) { + if (options.match && !matches(filename, options.match)) return next(); + if (options.exclude && matches(filename, options.exclude)) return next(); + if (options.filter && !options.filter(filename)) return next(); + + if (options.shortName){ + files.push(filename); + }else{ + files.push(file); + } + + fs.readFile(file, options.encoding, function(err, data) { + if (err) { + if (err.code === 'EACCES') return next(); + if (options.doneOnErr === true) { + return done(err); + } + } + if (callback.length > 3){ + if (options.shortName){ + callback(null, data, filename, next); + }else{ + callback(null, data, file, next); + } + }else{ + callback(null, data, next); + } + }); + } + else { + next(); + } + }); + })(); + + }); +} +module.exports = readFiles; diff --git a/lib_old/readfilesstream.js b/lib_old/readfilesstream.js new file mode 100644 index 0000000..eddd202 --- /dev/null +++ b/lib_old/readfilesstream.js @@ -0,0 +1,131 @@ +var fs = require('fs'), + mm = require('minimatch'), + path = require('path'); + +/** + * merge two objects by extending target object with source object + * @param target object to merge + * @param source object to merge + * @param {Boolean} [modify] whether to modify the target + * @returns {Object} extended object + */ +function extend(target, source, modify) { + var result = target ? modify ? target : extend({}, target, true) : {}; + if (!source) return result; + for (var key in source) { + if (source.hasOwnProperty(key) && source[key] !== undefined) { + result[key] = source[key]; + } + } + return result; +} + +/** + * determine if a string is contained within an array or matches a regular expression + * @param {String} str string to match + * @param {Array|Regex} match array or regular expression to match against + * @returns {Boolean} whether there is a match + */ +function matches(str, match) { + if (Array.isArray(match)) { + var l = match.length; + for( var s=0; s < l; s++) { + if ( mm(str,match[s])) { + return true; + } + } + return false; + } + return match.test(str); +} + +/** + * read files and call a function with the contents of each file + * @param {String} dir path of dir containing the files to be read + * @param {String} encoding file encoding (default is 'utf8') + * @param {Object} options options hash for encoding, recursive, and match/exclude + * @param {Function(error, string)} callback callback for each files content + * @param {Function(error)} complete fn to call when finished + */ +function readFilesStream(dir, options, callback, complete) { + if (typeof options === 'function') { + complete = callback; + callback = options; + options = {}; + } + if (typeof options === 'string') options = { + encoding: options + }; + options = extend({ + recursive: true, + encoding: 'utf8', + doneOnErr: true + }, options); + var files = []; + + var done = function(err) { + if (typeof complete === 'function') { + if (err) return complete(err); + complete(null, files); + } + }; + + fs.readdir(dir, function(err, list) { + if (err) { + if (options.doneOnErr === true) { + if (err.code === 'EACCES') return done(); + return done(err); + } + } + var i = 0; + + if (options.reverse === true || + (typeof options.sort == 'string' && + (/reverse|desc/i).test(options.sort))) { + list = list.reverse(); + } else if (options.sort !== false) list = list.sort(); + + (function next() { + var filename = list[i++]; + if (!filename) return done(null, files); + var file = path.join(dir, filename); + fs.stat(file, function(err, stat) { + if (err && options.doneOnErr === true) return done(err); + if (stat && stat.isDirectory()) { + if (options.recursive) { + if (options.matchDir && !matches(filename, options.matchDir)) return next(); + if (options.excludeDir && matches(filename, options.excludeDir)) return next(); + readFilesStream(file, options, callback, function(err, sfiles) { + if (err && options.doneOnErr === true) return done(err); + files = files.concat(sfiles); + next(); + }); + } else next(); + } else if (stat && stat.isFile()) { + if (options.match && !matches(filename, options.match)) return next(); + if (options.exclude && matches(filename, options.exclude)) return next(); + if (options.filter && !options.filter(filename)) return next(); + if (options.shortName) files.push(filename); + else files.push(file); + var stream = fs.createReadStream(file); + if (options.encoding !== null) { + stream.setEncoding(options.encoding); + } + stream.on('error',function(err) { + if (options.doneOnErr === true) return done(err); + next(); + }); + if (callback.length > 3) + if (options.shortName) callback(null, stream, filename, next); + else callback(null, stream, file, next); + else callback(null, stream, next); + } + else { + next(); + } + }); + })(); + + }); +} +module.exports = readFilesStream; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9ac0dfa --- /dev/null +++ b/package-lock.json @@ -0,0 +1,725 @@ +{ + "name": "path-reader", + "version": "2.0.2", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "path-reader", + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.4" + }, + "devDependencies": { + "@types/node": "^18.0.0", + "mocha": "3.5.0", + "should": "11.2.1", + "typescript": "^4.7.3" + }, + "engines": { + "node": ">= 0.10.5" + } + }, + "node_modules/@types/node": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", + "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "node_modules/brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "node_modules/commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "dependencies": { + "graceful-readlink": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "node_modules/growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, + "node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "deprecated": "Please use the native JSON object instead of JSON 3", + "dev": true + }, + "node_modules/lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true, + "dependencies": { + "lodash._basecopy": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "node_modules/lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "node_modules/lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", + "dev": true + }, + "node_modules/lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "node_modules/lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "node_modules/lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "dev": true, + "dependencies": { + "lodash._baseassign": "^3.0.0", + "lodash._basecreate": "^3.0.0", + "lodash._isiterateecall": "^3.0.0" + } + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "node_modules/lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "node_modules/lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "dependencies": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz", + "integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==", + "dev": true, + "dependencies": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 0.10.x", + "npm": ">= 1.4.x" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/should": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/should/-/should-11.2.1.tgz", + "integrity": "sha1-kPVRRVUtAc/CAGZuToGKHJZw7aI=", + "dev": true, + "dependencies": { + "should-equal": "^1.0.0", + "should-format": "^3.0.2", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" + } + }, + "node_modules/should-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-1.0.1.tgz", + "integrity": "sha1-C26VFvJgGp+wuy3MNpr6HH4gCvc=", + "dev": true, + "dependencies": { + "should-type": "^1.0.0" + } + }, + "node_modules/should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", + "dev": true, + "dependencies": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, + "node_modules/should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", + "dev": true + }, + "node_modules/should-type-adaptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.0.1.tgz", + "integrity": "sha1-7+VVPN9oz/ZuXF9RtxLcNRx3vqo=", + "dev": true, + "dependencies": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "node_modules/should-util": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz", + "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", + "dev": true + }, + "node_modules/supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/typescript": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", + "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + }, + "dependencies": { + "@types/node": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", + "integrity": "sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "dev": true, + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "dev": true, + "requires": { + "lodash._baseassign": "^3.0.0", + "lodash._basecreate": "^3.0.0", + "lodash._isiterateecall": "^3.0.0" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz", + "integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "should": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/should/-/should-11.2.1.tgz", + "integrity": "sha1-kPVRRVUtAc/CAGZuToGKHJZw7aI=", + "dev": true, + "requires": { + "should-equal": "^1.0.0", + "should-format": "^3.0.2", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" + } + }, + "should-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-1.0.1.tgz", + "integrity": "sha1-C26VFvJgGp+wuy3MNpr6HH4gCvc=", + "dev": true, + "requires": { + "should-type": "^1.0.0" + } + }, + "should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", + "dev": true, + "requires": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, + "should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", + "dev": true + }, + "should-type-adaptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.0.1.tgz", + "integrity": "sha1-7+VVPN9oz/ZuXF9RtxLcNRx3vqo=", + "dev": true, + "requires": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "should-util": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz", + "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", + "dev": true + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + }, + "typescript": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz", + "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 8961e0a..a49ec73 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,27 @@ { - "name": "node-dir", - "version": "0.1.17", + "name": "path-reader", + "version": "2.0.2", "description": "asynchronous file and directory operations for Node.js", - "main": "index", - "homepage": "https://github.com/fshost", - "repository": "https://github.com/fshost/node-dir", - "author": { - "name": "Nathan Cartwright", - "email": "fshost@yahoo.com", - "url": "https://github.com/fshost" - }, + "main": "lib/index", + "typings": "lib/index.d.ts", + "homepage": "https://github.com/ackerapple", + "repository": "https://github.com/ackerapple/path-reader", "directories": { "lib": "lib" }, "scripts": { - "test": "./node_modules/.bin/mocha --reporter spec" + "build": "rm -rf lib && tsc --project ./ts/tsconfig.json", + "test": "./node_modules/.bin/mocha --reporter spec", + "version:patch": "npm --no-git-tag-version version patch", + "save": "npm run build && npm run test && npm run version:patch && git add . && git commit -m \"update\" && git push", + "deploy": "npm run save && npm publish" }, "engines": { "node": ">= 0.10.5" }, "license": "MIT", "keywords": [ - "node-dir", + "path-reader", "directory", "dir", "subdir", @@ -31,10 +31,14 @@ "fs" ], "dependencies": { - "minimatch": "^3.0.2" + "minimatch": "^3.0.4" + }, + "optionalDependencies": { + "@types/node": "^18.0.0" }, "devDependencies": { - "mocha": "~1.13.0", - "should": "~2.0.2" + "mocha": "3.5.0", + "should": "11.2.1", + "typescript": "^4.7.3" } } diff --git "a/test/fixtures/testdir5/testu\347\365es.txt" b/test/fixtures/testdir5/testu%E7%F5es.txt similarity index 100% rename from "test/fixtures/testdir5/testu\347\365es.txt" rename to test/fixtures/testdir5/testu%E7%F5es.txt diff --git a/test/test.js b/test/test.js index a4d6ce8..a8f5d87 100644 --- a/test/test.js +++ b/test/test.js @@ -1,1278 +1,1403 @@ -var path = require('path'), - should = require('should'), - dir = require('..'), - fixturesDir = path.join(__dirname, 'fixtures'), - tdir = path.join(fixturesDir, 'testdir'), - tdir2 = path.join(fixturesDir, 'testdir2'), - tdir3 = path.join(fixturesDir, 'testdir3'), - tdir4 = path.join(fixturesDir, 'testdir4'), - tdir5 = path.join(fixturesDir, 'testdir5'); +var fs = require('fs') +var path = require('path') +var should = require('should') +var dir = require('..') +var fixturesDir = path.join(__dirname, 'fixtures') + +var tdir = path.join(fixturesDir, 'testdir') +var tdir2 = path.join(fixturesDir, 'testdir2') +var tdir3 = path.join(fixturesDir, 'testdir3') +var tdir4 = path.join(fixturesDir, 'testdir4') +var tdir5 = path.join(fixturesDir, 'testdir5') +//empty dirs that may not exist +var tdir6 = path.join(fixturesDir, 'testdir6') +var tdir7 = path.join(fixturesDir, 'testdir7') +var tdir8 = path.join(fixturesDir, 'testdir7', 'testdir8') + +var assert = require('assert') +var isWin = require('os').type()=='Windows_NT' +var winIt = isWin ? it.skip : it//skip all symlink based testing + +//param empty folder +function paramDir(pathTo){ + try{ + fs.mkdirSync( pathTo ) + }catch(e){ + if( !e.code || e.code!='EEXIST' ){ + throw e + } + } +} + +paramDir( tdir6 ) +paramDir( tdir7 ) +paramDir( tdir8 ) describe('readfiles method', function() { - it('should pass the contents of every file to a callback', function(done) { - dir.readFiles( - tdir, function(err, content, filename, next) { - should.not.exist(err); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.replace(/\r/g, '').should.equal(expected); - next(); - }, function() { - done(); - }); - }); - - it('should invoke a done callback after processing all files', function(done) { - var filenames = []; - dir.readFiles( - tdir, function(err, content, filename, next) { - should.not.exist(err); - should.exist(content); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }).sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('should read files in sorted order if the sort option is set to true', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - sort: true - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('should read files in sorted order (per directory) if the sort option is set to true', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - sort: true - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - filenames.should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('should read files in reverse order (per directory) if the reverse option is set to true', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - reverse: true - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.should.eql([ - 'testdir/subdir/file4.text', - 'testdir/subdir/file3.txt', - 'testdir/file2.text', - 'testdir/file1.txt' - ]); - filenames.should.eql(['file4', 'file3', 'file2', 'file1']); - done(); - }); - }); - - it('should apply a filter to the files if a filter option is specified', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - filter: function(filename) { - return~ filename.search('file1') || ~filename.search('file2'); - } - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text' - ]); - filenames.should.eql(['file1', 'file2']); - done(); - }); - }); - - it('should accept an string argument that can specify encoding', function(done) { - var filenames = []; - dir.readFiles( - tdir, 'ascii', function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('should accept an options argument that can specify encoding', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - encoding: 'ascii' - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('if shortName option is true, only aggregate the base filename rather than the full filepath', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - shortName: true - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - path.basename(filename).should.equal(filename); - var shortName = filename.replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(filename); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1.txt', 'file2.text', 'file3.txt', 'file4.text']); - done(); - }); - }); - - it('if recursive option is set to false, should not read files in subdirectories', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - recursive: false - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text' - ]); - filenames.sort().should.eql(['file1', 'file2']); - done(); - }); - }); - - it('if given a match regex option, should only read files that match it', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - match: /txt$/ - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file3']); - done(); - }); - }); - - it('if given a match array option, should only read files that match an item in the array', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - match: ['file1.txt', 'file3.txt'] - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file3']); - done(); - }); - }); - - it('match option should match regex pattern only to the filename itself, not the full filepath', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - match: /^file/ - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('if given an exclude regex option, should only read files that do not match the exclude pattern', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - exclude: /text$/ - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file3']); - done(); - }); - }); - - it('if given an exclude array option, should only read files that do not match any items in the array', function(done) { - var filenames = []; - dir.readFiles( - tdir, { - exclude: ['file2.text', 'file4.text'] - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file3']); - done(); - }); - }); - - it('if given a matchDir regex option, should only read files in subdirectories that match it', function(done) { - var filenames = []; - dir.readFiles( - tdir2, { - matchDir: /special/i - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file3', 'file4']); - done(); - }); - }); - - it('if given a matchDir array option, should only read files in subdirectories that match an item in the array', function(done) { - var filenames = []; - dir.readFiles( - tdir2, { - matchDir: ['special_files', 'nonexistent'] - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file3', 'file4']); - done(); - }); - }); - - it('if given an excludeDir regex option, should only read files that are not in subdirectories that match the exclude pattern', function(done) { - var filenames = []; - dir.readFiles( - tdir2, { - excludeDir: /^\./ - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file2', 'file3', 'file4']); - done(); - }); - }); - - it('if given an excludeDir array option, should only read files that are in subdirectories that do not match any item in the array', function(done) { - var filenames = []; - dir.readFiles( - tdir2, { - excludeDir: ['.bin', '.nonexistent'] - }, function(err, content, filename, next) { - should.not.exist(err); - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file2', 'file3', 'file4']); - done(); - }); - }); - - it('should done on error', function(done) { - dir.readFiles( - tdir3, function(err, content, filename, next) { - should.not.exist(err); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.replace(/\r/g, '').should.equal(expected); - next(); - }, function(err) { - should.exist(err); - done(); - }); - }); - - it('if given doneOnErr to false, should not done on error', function(done) { - dir.readFiles( - tdir3, { doneOnErr: false },function(err, content, filename, next) { - should.not.exist(err); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.replace(/\r/g, '').should.equal(expected); - next(); - }, function() { - done(); - }); - }); - - it('can be called with a callback in which the filename argument is omitted', function(done) { - dir.readFiles( - tdir, function(err, content, next) { - should.not.exist(err); - content.should.be.a.string; - content.indexOf('begin content of').should.equal(0); - next(); - }, function(err) { - should.not.exist(err); - done(); - }); - }); - - it('can be called with the done callback argument omitted', function(done) { - var i = 0; - dir.readFiles( - tdir, function(err, content, next) { - should.not.exist(err); - next(); - i++; - if (i === 4) done(); - }); - }); + it('should pass the contents of every file to a callback', function(done) { + dir.readFiles( + tdir, function(err, content, filename, next) { + should.not.exist(err); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.replace(/\r/g, '').should.equal(expected); + next(); + }, function() { + done(); + }); + }); + + it('should invoke a done callback after processing all files', function(done) { + var filenames = []; + dir.readFiles( + tdir, function(err, content, filename, next) { + should.not.exist(err); + should.exist(content); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }).sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('should read files in sorted order if the sort option is set to true', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + sort: true + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('should read files in sorted order (per directory) if the sort option is set to true', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + sort: true + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + filenames.should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('should read files in reverse order (per directory) if the reverse option is set to true', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + reverse: true + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.should.eql([ + 'testdir'+path.sep+'subdir'+path.sep+'file4.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'file1.txt' + ]); + filenames.should.eql(['file4', 'file3', 'file2', 'file1']); + done(); + }); + }); + + it('should apply a filter to the files if a filter option is specified', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + filter: function(filename) { + return~ filename.search('file1') || ~filename.search('file2'); + } + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text' + ]); + filenames.should.eql(['file1', 'file2']); + done(); + }); + }); + + it('should accept an string argument that can specify encoding', function(done) { + var filenames = []; + dir.readFiles( + tdir, 'ascii', function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('should accept an options argument that can specify encoding', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + encoding: 'ascii' + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('if shortName option is true, only aggregate the base filename rather than the full filepath', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + shortName: true + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + path.basename(filename).should.equal(filename); + var shortName = filename.replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(filename); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1.txt', 'file2.text', 'file3.txt', 'file4.text']); + done(); + }); + }); + + it('if recursive option is set to false, should not read files in subdirectories', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + recursive: false + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text' + ]); + filenames.sort().should.eql(['file1', 'file2']); + done(); + }); + }); + + it('if given a match regex option, should only read files that match it', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + match: /txt$/ + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file3']); + done(); + }); + }); + + it('if given a match array option, should only read files that match an item in the array', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + match: ['file1.txt', 'file3.txt'] + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file3']); + done(); + }); + }); + + it('match option should match regex pattern only to the filename itself, not the full filepath', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + match: /^file/ + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('if given an exclude regex option, should only read files that do not match the exclude pattern', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + exclude: /text$/ + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file3']); + done(); + }); + }); + + it('if given an exclude array option, should only read files that do not match any items in the array', function(done) { + var filenames = []; + dir.readFiles( + tdir, { + exclude: ['file2.text', 'file4.text'] + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file3']); + done(); + }); + }); + + it('if given a matchDir regex option, should only read files in subdirectories that match it', function(done) { + var filenames = []; + dir.readFiles( + tdir2, { + matchDir: /special/i + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file3', 'file4']); + done(); + }); + }); + + it('if given a matchDir array option, should only read files in subdirectories that match an item in the array', function(done) { + var filenames = []; + dir.readFiles( + tdir2, { + matchDir: ['special_files', 'nonexistent'] + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file3', 'file4']); + done(); + }); + }); + + it('if given an excludeDir regex option, should only read files that are not in subdirectories that match the exclude pattern', function(done) { + var filenames = []; + dir.readFiles( + tdir2, { + excludeDir: /^\./ + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file2', 'file3', 'file4']); + done(); + }); + }); + + it('if given an excludeDir array option, should only read files that are in subdirectories that do not match any item in the array', function(done) { + var filenames = []; + dir.readFiles( + tdir2, { + excludeDir: ['.bin', '.nonexistent'] + }, function(err, content, filename, next) { + should.not.exist(err); + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file2', 'file3', 'file4']); + done(); + }); + }); + + winIt('should done on error', function(done) { + dir.readFiles( + tdir3, function(err, content, filename, next) { + if(filename.split(path.sep).pop()!='file1.txt')return next() + should.not.exist(err); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.replace(/\r/g, '').should.equal(expected); + next(); + }, function(err) { + should.exist(err); + done(); + }); + }); + + it('if given doneOnErr to false, should not done on error', function(done) { + dir.readFiles( + tdir3, { doneOnErr: false },function(err, content, filename, next) { + if(filename.split(path.sep).pop()!='file1.txt')return next() + should.not.exist(err); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.replace(/\r/g, '').should.equal(expected); + next(); + }, function() { + done(); + }); + }); + + it('can be called with a callback in which the filename argument is omitted', function(done) { + dir.readFiles( + tdir, function(err, content, next) { + should.not.exist(err); + content.should.be.a.string; + content.indexOf('begin content of').should.equal(0); + next(); + }, function(err) { + should.not.exist(err); + done(); + }); + }); + + it('can be called with the done callback argument omitted', function(done) { + var i = 0; + dir.readFiles( + tdir, function(err, content, next) { + should.not.exist(err); + next(); + i++; + if (i === 4) done(); + }); + }); }); describe('readfilesstream method', function() { - it('should pass the stream of every file to a callback', function(done) { - dir.readFilesStream( - tdir, function(err, stream, filename, next) { - should.not.exist(err); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content.replace(/\r/g, '').should.equal(expected); - next(); - }); - - }, function() { - done(); - }); - }); - it('should invoke a done callback after processing all files', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, function(err, stream, filename, next) { - should.not.exist(err); - should.exist(stream); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - filenames.push(shortName); - next(); - }, function(err, files) { - should.not.exist(err); - files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }).sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('should read files in sorted order if the sort option is set to true', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - sort: true - }, function(err, stream, filename, next) { - should.not.exist(err); - //content = content.replace(/\r/g, ''); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('should read files in sorted order (per directory) if the sort option is set to true', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - sort: true - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - filenames.should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('should read files in reverse order (per directory) if the reverse option is set to true', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - reverse: true - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.should.eql([ - 'testdir/subdir/file4.text', - 'testdir/subdir/file3.txt', - 'testdir/file2.text', - 'testdir/file1.txt' - ]); - filenames.should.eql(['file4', 'file3', 'file2', 'file1']); - done(); - }); - }); - - it('should apply a filter to the files if a filter option is specified', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - filter: function(filename) { - return~ filename.search('file1') || ~filename.search('file2'); - } - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text' - ]); - filenames.should.eql(['file1', 'file2']); - done(); - }); - }); - - it('should accept an string argument that can specify encoding', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, 'ascii', function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('should accept an options argument that can specify encoding', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - encoding: 'ascii' - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('if shortName option is true, only aggregate the base filename rather than the full filepath', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - shortName: true - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - path.basename(filename).should.equal(filename); - var shortName = filename.replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(filename); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1.txt', 'file2.text', 'file3.txt', 'file4.text']); - done(); - }); - }); - - it('if recursive option is set to false, should not read files in subdirectories', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - recursive: false - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text' - ]); - filenames.sort().should.eql(['file1', 'file2']); - done(); - }); - }); - - it('if given a match regex option, should only read files that match it', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - match: /txt$/ - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file3']); - done(); - }); - }); - - it('if given a match array option, should only read files that match an item in the array', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - match: ['file1.txt', 'file3.txt'] - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file3']); - done(); - }); - }); - - it('match option should match regex pattern only to the filename itself, not the full filepath', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - match: /^file/ - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - filenames.push(shortName); - content.should.equal(expected); - next(); - }); - }, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); - done(); - }); - }); - - it('if given an exclude regex option, should only read files that do not match the exclude pattern', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - exclude: /text$/ - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file3']); - done(); - }); - }); - - it('if given an exclude array option, should only read files that do not match any items in the array', function(done) { - var filenames = []; - dir.readFilesStream( - tdir, { - exclude: ['file2.text', 'file4.text'] - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file1', 'file3']); - done(); - }); - }); - - it('if given a matchDir regex option, should only read files in subdirectories that match it', function(done) { - var filenames = []; - dir.readFilesStream( - tdir2, { - matchDir: /special/i - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file3', 'file4']); - done(); - }); - }); - - it('if given a matchDir array option, should only read files in subdirectories that match an item in the array', function(done) { - var filenames = []; - dir.readFilesStream( - tdir2, { - matchDir: ['special_files', 'nonexistent'] - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file3', 'file4']); - done(); - }); - }); - - it('if given an excludeDir regex option, should only read files that are not in subdirectories that match the exclude pattern', function(done) { - var filenames = []; - dir.readFilesStream( - tdir2, { - excludeDir: /^\./ - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file2', 'file3', 'file4']); - done(); - }); - }); - - it('if given an excludeDir array option, should only read files that are in subdirectories that do not match any item in the array', function(done) { - var filenames = []; - dir.readFilesStream( - tdir2, { - excludeDir: ['.bin', '.nonexistent'] - }, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err, files) { - should.not.exist(err); - filenames.sort().should.eql(['file2', 'file3', 'file4']); - done(); - }); - }); - - it('should done on error', function(done) { - dir.readFilesStream( - tdir3, function(err, stream, filename, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content = content.replace(/\r/g, ''); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - content.should.equal(expected); - filenames.push(shortName); - next(); - }); - }, function(err) { - should.exist(err); - done(); - }); - }); + it('should pass the stream of every file to a callback', function(done) { + dir.readFilesStream( + tdir, function(err, stream, filename, next) { + should.not.exist(err); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content.replace(/\r/g, '').should.equal(expected); + next(); + }); + + }, function() { + done(); + }); + }); + it('should invoke a done callback after processing all files', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, function(err, stream, filename, next) { + should.not.exist(err); + should.exist(stream); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + filenames.push(shortName); + next(); + }, function(err, files) { + should.not.exist(err); + files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }).sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('should read files in sorted order if the sort option is set to true', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + sort: true + }, function(err, stream, filename, next) { + should.not.exist(err); + //content = content.replace(/\r/g, ''); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('should read files in sorted order (per directory) if the sort option is set to true', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + sort: true + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + filenames.should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('should read files in reverse order (per directory) if the reverse option is set to true', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + reverse: true + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.should.eql([ + 'testdir'+path.sep+'subdir'+path.sep+'file4.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'file1.txt' + ]); + filenames.should.eql(['file4', 'file3', 'file2', 'file1']); + done(); + }); + }); + + it('should apply a filter to the files if a filter option is specified', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + filter: function(filename) { + return~ filename.search('file1') || ~filename.search('file2'); + } + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text' + ]); + filenames.should.eql(['file1', 'file2']); + done(); + }); + }); + + it('should accept an string argument that can specify encoding', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, 'ascii', function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('should accept an options argument that can specify encoding', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + encoding: 'ascii' + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('if shortName option is true, only aggregate the base filename rather than the full filepath', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + shortName: true + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + path.basename(filename).should.equal(filename); + var shortName = filename.replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(filename); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1.txt', 'file2.text', 'file3.txt', 'file4.text']); + done(); + }); + }); + + it('if recursive option is set to false, should not read files in subdirectories', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + recursive: false + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text' + ]); + filenames.sort().should.eql(['file1', 'file2']); + done(); + }); + }); + + it('if given a match regex option, should only read files that match it', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + match: /txt$/ + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file3']); + done(); + }); + }); + + it('if given a match array option, should only read files that match an item in the array', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + match: ['file1.txt', 'file3.txt'] + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file3']); + done(); + }); + }); + + it('match option should match regex pattern only to the filename itself, not the full filepath', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + match: /^file/ + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + filenames.push(shortName); + content.should.equal(expected); + next(); + }); + }, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + filenames.sort().should.eql(['file1', 'file2', 'file3', 'file4']); + done(); + }); + }); + + it('if given an exclude regex option, should only read files that do not match the exclude pattern', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + exclude: /text$/ + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file3']); + done(); + }); + }); + + it('if given an exclude array option, should only read files that do not match any items in the array', function(done) { + var filenames = []; + dir.readFilesStream( + tdir, { + exclude: ['file2.text', 'file4.text'] + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file1', 'file3']); + done(); + }); + }); + + it('if given a matchDir regex option, should only read files in subdirectories that match it', function(done) { + var filenames = []; + dir.readFilesStream( + tdir2, { + matchDir: /special/i + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file3', 'file4']); + done(); + }); + }); + + it('if given a matchDir array option, should only read files in subdirectories that match an item in the array', function(done) { + var filenames = []; + dir.readFilesStream( + tdir2, { + matchDir: ['special_files', 'nonexistent'] + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file3', 'file4']); + done(); + }); + }); + + it('if given an excludeDir regex option, should only read files that are not in subdirectories that match the exclude pattern', function(done) { + var filenames = []; + dir.readFilesStream( + tdir2, { + excludeDir: /^\./ + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file2', 'file3', 'file4']); + done(); + }); + }); + + it('if given an excludeDir array option, should only read files that are in subdirectories that do not match any item in the array', function(done) { + var filenames = []; + dir.readFilesStream( + tdir2, { + excludeDir: ['.bin', '.nonexistent'] + }, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err, files) { + should.not.exist(err); + filenames.sort().should.eql(['file2', 'file3', 'file4']); + done(); + }); + }); + + winIt('should done on error', function(done) { + dir.readFilesStream( + tdir3, function(err, stream, filename, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content = content.replace(/\r/g, ''); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + content.should.equal(expected); + filenames.push(shortName); + next(); + }); + }, function(err) { + should.exist(err); + done(); + }); + }); + + it('if given doneOnErr to false, should not done on error', function(done) { + dir.readFilesStream( + tdir3, { doneOnErr: false },function(err, stream, filename, next) { + if(filename.split(path.sep).pop()!='file1.txt')return next() + should.not.exist(err); + var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); + var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content.replace(/\r/g, '').should.equal(expected); + next(); + }); + }, function() { + done(); + }); + }); + + it('can be called with a callback in which the filename argument is omitted', function(done) { + dir.readFilesStream( + tdir, function(err, stream, next) { + should.not.exist(err); + var content = ''; + stream.on('data',function(buffer) { + var part = buffer.toString(); + content += part; + }); + stream.on('end',function() { + content.should.be.a.string; + content.indexOf('begin content of').should.equal(0); + next(); + }); + }, function(err) { + should.not.exist(err); + done(); + }); + }); + + it('can be called with the done callback argument omitted', function(done) { + var i = 0; + dir.readFilesStream( + tdir, function(err, stream, next) { + should.not.exist(err); + next(); + i++; + if (i === 4) done(); + }); + }); +}); - it('if given doneOnErr to false, should not done on error', function(done) { - dir.readFilesStream( - tdir3, { doneOnErr: false },function(err, stream, filename, next) { - should.not.exist(err); - var shortName = path.basename(filename).replace(new RegExp(path.extname(filename) + '$'), ''); - var expected = 'begin content of ' + shortName + '\ncontent body\nend content of ' + shortName; - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content.replace(/\r/g, '').should.equal(expected); - next(); - }); - }, function() { - done(); - }); - }); +describe('#promiseFiles',function(){ + it("#promiseFiles", function(done) { + dir.promiseFiles(tdir) + .then(function(files) { + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + }) + .then(done).catch(done) + }); - it('can be called with a callback in which the filename argument is omitted', function(done) { - dir.readFilesStream( - tdir, function(err, stream, next) { - should.not.exist(err); - var content = ''; - stream.on('data',function(buffer) { - var part = buffer.toString(); - content += part; - }); - stream.on('end',function() { - content.should.be.a.string; - content.indexOf('begin content of').should.equal(0); - next(); - }); - }, function(err) { - should.not.exist(err); - done(); - }); - }); + it("#promiseFiles(path, {shortName:true})", function(done) { + dir.promiseFiles(tdir, {shortName:true}) + .then(function(files) { + assert.deepEqual(files.sort(), [ + 'file1.txt', + 'file2.text', + 'file3.txt', + 'file4.text' + ]) + }) + .then(done).catch(done) + }); - it('can be called with the done callback argument omitted', function(done) { - var i = 0; - dir.readFilesStream( - tdir, function(err, stream, next) { - should.not.exist(err); - next(); - i++; - if (i === 4) done(); - }); - }); -}); + it("#promiseFiles(path, 'combine', {shortName:true})", function(done) { + dir.promiseFiles(tdir, 'combine', {shortName:true}) + .then(function(files) { + assert.deepEqual(files.sort(), [ + 'file1.txt', + 'file2.text', + 'file3.txt', + 'file4.text', + 'subdir' + ]) + }) + .then(done).catch(done) + }); -it("#promiseFiles", function(done) { - dir.promiseFiles(tdir) + it("#promiseFiles(path, 'combine', {shortName:'relative'})", function(done) { + dir.promiseFiles(tdir, 'combine', {shortName:'relative'}) .then(function(files) { - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); + assert.deepEqual(files.sort(), [ + 'file1.txt', + 'file2.text', + 'subdir', + 'subdir'+path.sep+'file3.txt', + 'subdir'+path.sep+'file4.text' + ]) }) .then(done).catch(done) -}); + }); +}) -describe("files method", function() { - it("#files(path, {sync:true}", - function() { - var files = dir.files(tdir,'file',()=>{},{sync:true}); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); +describe("files method", function() { - relFiles.sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); + it("#files(path, {sync:true})", + function() { + var files = dir.files(tdir,'file',function(){},{sync:true}); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); }); - it("should iterate the files of a directory (recursively) and pass their filenames to a callback", function(done) { - dir.files(tdir, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - done(); - }); + relFiles.sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + }); + + it("should iterate the files of a directory (recursively) and pass their filenames to a callback", function(done) { + dir.files(tdir, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + done(); }); - - it("should return broken symlinks as files", function(done) { - dir.files(tdir3, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.sort().should.eql([ - 'testdir3/broken_link.txt', - 'testdir3/file1.txt' - ]); - done(); - }); + }); + + it("should return broken symlinks as files", function(done) { + dir.files(tdir3, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.sort().should.eql([ + 'testdir3'+path.sep+'broken_link.txt', + 'testdir3'+path.sep+'file1.txt' + ]); + done(); }); - - it("should iterate files of symlinked directories (recursively)", function(done) { - dir.files(tdir4, function(err, files) { - should.not.exist(err); - var relFiles = files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.sort().should.eql([ - 'testdir4/testdir/file1.txt', - 'testdir4/testdir/file2.text', - 'testdir4/testdir/subdir/file3.txt', - 'testdir4/testdir/subdir/file4.text' - ]); - done(); - }); + }); + + winIt("should iterate files of symlinked directories (recursively)", function(done) { + dir.files(tdir4, function(err, files) { + should.not.exist(err); + var relFiles = files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + var testArray = [ + 'testdir4'+path.sep+'testdir'+path.sep+'file1.txt', + 'testdir4'+path.sep+'testdir'+path.sep+'file2.text', + 'testdir4'+path.sep+'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir4'+path.sep+'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ] + relFiles.sort().should.eql(testArray); + done(); }); + }); + + // NOT supported everywhere. Real hard to get working + it.skip("support non-UTF8 file names", function() { + var files = dir.files(tdir5,'file', function(){}, {sync:true, excludeHidden:true}); + var cmp = Buffer.from('testdir5'+path.sep+'testuções.txt', 'latin1').toString(); + + var relFile = path.relative(fixturesDir, files[0]) + relFile.should.eql(cmp)//This test does not pass on all Systems + }); + + describe('empty-folders',function(){ + it("empty-folder", function() { + var files = dir.files(tdir6,'file', function(){}, {sync:true, excludeHidden:true}); + assert.equal(files.length, 0) + }) - it("support non-UTF8 file names", function() { - var files = dir.files(tdir5,'file',()=>{},{sync:true}); - var cmp = Buffer.from('testdir5/testuções.txt', 'latin1').toString(); - path.relative(fixturesDir, files[0]).should.eql(cmp); - }); -}); + it("folder-with-folder-only", function() { + var files = dir.files(tdir7,'file', function(){}, {sync:true, excludeHidden:true}); + assert.equal(files.length, 0) + }) + it("folder-with-folder-only-promise", function(done) { + dir.promiseFiles(tdir7,'file', {excludeHidden:true}) + .then(function(files){ + assert.equal(files.length, 0) + }) + .then(done).catch(done) + }) + it("empty-folder-promise", function(done) { + dir.promiseFiles(tdir6,'file', {excludeHidden:true}) + .then(function(files){ + assert.equal(files.length, 0) + }) + .then(done).catch(done) + }) -describe('subdirs method', function() { - it('should pass an array of the subdir paths of every subdir in a directory (recursive) to a callback', function(done) { - dir.subdirs(tdir, function(err, dirs) { - should.not.exist(err); - var relPaths = dirs.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relPaths.length.should.equal(1); - relPaths[0].should.equal('testdir/subdir'); - done(); - }); + it("empty-folder-callback", function(done) { + dir.files(tdir6,'file', function(err,files){ + if(err)return done(err) + assert.equal(files.length, 0) + done() + }, {excludeHidden:true}) + }) + }) +}) +describe('subdirs method', function() { + it('should pass an array of the subdir paths of every subdir in a directory (recursive) to a callback', function(done) { + dir.subdirs(tdir, function(err, dirs) { + should.not.exist(err); + var relPaths = dirs.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relPaths.length.should.equal(1); + relPaths[0].should.equal('testdir'+path.sep+'subdir'); + done(); }); + + }); }); describe('paths method', function() { - it('should pass an object with a files property and dirs property of the paths of every file and subdir, respectively, in a directory (recursive) to a callback', function(done) { - dir.paths(tdir, function(err, paths) { - should.not.exist(err); - paths.should.be.a.object; - paths.should.not.be.a.array; - should.exist(paths.files); - should.exist(paths.dirs); - var relFiles = paths.files.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - var relPaths = paths.dirs.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relFiles.sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - relPaths.length.should.equal(1); - relPaths[0].should.equal('testdir/subdir'); - done(); - }); + it('should pass an object with a files property and dirs property of the paths of every file and subdir, respectively, in a directory (recursive) to a callback', function(done) { + dir.paths(tdir, function(err, paths) { + should.not.exist(err); + paths.should.be.a.object; + paths.should.not.be.a.array; + should.exist(paths.files); + should.exist(paths.dirs); + var relFiles = paths.files.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + var relPaths = paths.dirs.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }); + relFiles.sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]); + relPaths.length.should.equal(1); + relPaths[0].should.equal('testdir'+path.sep+'subdir'); + done(); }); - - describe('when called with combine argument set to true', function() { - - it('should pass an array of filepaths of all subdirs and files in a directory and its subdirs to a callback', function(done) { - dir.paths(tdir, true, function(err, paths) { - should.not.exist(err); - - paths.should.be.a.array; - var relPaths = paths.map(function(curPath) { - return path.relative(fixturesDir, curPath); - }); - relPaths.sort().should.eql([ - 'testdir/file1.txt', - 'testdir/file2.text', - 'testdir/subdir', - 'testdir/subdir/file3.txt', - 'testdir/subdir/file4.text' - ]); - }); - done(); - }); - - }); - + }); + + describe('when called with combine argument set to true', function(){ + it('should pass an array of filepaths of all subdirs and files in a directory and its subdirs to a callback', function(done) { + dir.paths(tdir, true, function(err, paths) { + should.not.exist(err) + + paths.should.be.a.array; + var relPaths = paths.map(function(curPath) { + return path.relative(fixturesDir, curPath); + }) + + relPaths.sort().should.eql([ + 'testdir'+path.sep+'file1.txt', + 'testdir'+path.sep+'file2.text', + 'testdir'+path.sep+'subdir', + 'testdir'+path.sep+'subdir'+path.sep+'file3.txt', + 'testdir'+path.sep+'subdir'+path.sep+'file4.text' + ]) + }) + done() + }) + }) + + describe('valuetizer',function(){ + it('files by mtime', function(done) { + var foundMtime = false + var options = { + valuetizer:function(stat, shortName, longPath){ + return stat.mtime && !foundMtime ? foundMtime=true && stat : null + } + } + + dir.promiseFiles(tdir, 'file', options) + .then(function(results){ + assert.equal(results.length, 1) + }) + .then(done).catch(done) + }) + }) }); diff --git a/ts/index.ts b/ts/index.ts new file mode 100644 index 0000000..d55b44f --- /dev/null +++ b/ts/index.ts @@ -0,0 +1,3 @@ +export * from './paths' +export { readFiles } from './readfiles' +export { readFilesStream } from './readfilesstream' diff --git a/ts/paths.ts b/ts/paths.ts new file mode 100644 index 0000000..3467f65 --- /dev/null +++ b/ts/paths.ts @@ -0,0 +1,341 @@ +import * as fs from 'fs' +import * as path from 'path' + + +interface FileReadOptions { + basePath?: string + recursive?: boolean + excludeHidden?: boolean + sync?: boolean + ignoreType?: string + valuetizer?: (stat, shortName: string, longPath: string, isDir?: boolean) => any +} + +export function promiseFiles( + dir, + type, + options?: FileReadOptions, + statOptions?: fs.StatSyncOptions, +){ + switch(typeof type){ + case 'object': + options = type + type = 'file' + break; + + default:type = type || 'file' + } + + var processor = function(res,rej){ + var cb = function(err,data){ + if(err)return rej(err) + res(data) + } + exports.files(dir, type, cb, options, statOptions) + } + return new Promise(processor) +} + +/** + * find all files or subdirs (recursive) and pass to callback fn + * + * @param {string} dir directory in which to recurse files or subdirs + * @param {string} type type of dir entry to recurse ('file', 'dir', or 'all', defaults to 'file') + * @param {function(error, )} callback fn to call when done + * @example + * dir.files(__dirname, function(err, files) { + * if (err) throw err; + * console.log('files:', files); + * }); + */ +export function files( + dir, type, callback, + options?: FileReadOptions, + statOptions?: fs.StatSyncOptions, +) { + var ofType = typeof type + if(ofType == 'object'){ + options = options || type + type = 'file' + callback = function(){} + }else if (ofType !== 'string') { + //ignoreType = callback; + callback = type; + type = 'file'; + } + + options = options || {} as FileReadOptions + + var pending + var results = { + files: [], + dirs: [] + }; + + var done = function(_list?: any) { + if(type==='combine'){ + results = results.files.concat(results.dirs) as any + } else if (!type || options.ignoreType || ['all','combine'].indexOf(type)>=0) { + results = results + } else { + results = results[type + 's'] + } + + if(options.sync)return; + + callback(null, results); + }; + + /** + @statPath - fullPath + @name - fileName + @statHanOptions - { + valuetizer:function(stat, shortName, longPath){}, - function the handles value assignment + lstatCalled:false - used internally + } + */ + var getStatHandler = function( + statPath, + name, + statHanOptions, + ) { + return function(err, stat) { + if (err) { + if (!statHanOptions.lstatCalled) { + var newStatHanOptions = assign(statHanOptions, {lstatCalled:true}) + if(options.sync){ + var lstat = fs.lstatSync(statPath); + return getStatHandler(statPath, name, newStatHanOptions)(null, lstat) + }else{ + return fs.lstat(statPath, getStatHandler(statPath, name, newStatHanOptions)); + } + } + return callback(err); + } + + var isDir = stat && stat.isDirectory() && stat.mode !== 17115 + var pushVal = statHanOptions.valuetizer(stat, name, statPath, isDir)//options.shortName ? name : statPath + + if( pushVal==null ){ + if (!--pending){ + done(); + } + return + } + + if (isDir) { + if (type !== 'file') { + results.dirs.push(pushVal); + } + + if (options.recursive==null || options.recursive) { + var subloop = function(err, res) { + if (err){ + return callback(err) + } + + if(type === 'combine'){ + results.files = results.files.concat(res); + }else if (type === 'all') { + if( res.files ){//dont add things that are not there + results.files = results.files.concat(res.files); + } + results.dirs = results.dirs.concat(res.dirs); + } else if (type === 'file') { + if( res.files ){//dont add things that are not there + results.files = results.files.concat(res.files); + } + } else { + results.dirs = results.dirs.concat(res.dirs); + } + + if (!--pending){ + done(); + } + } + + var newOptions = assign({}, options) + newOptions.basePath = options.basePath || dir + newOptions.ignoreType = true + var moreResults = files(statPath, type, subloop, newOptions); + + if(options.sync){ + subloop(null, moreResults) + } + }else if (!--pending){ + done() + } + } else { + var excludeHidden = options.excludeHidden && name.split(path.sep).pop().search(/^\./)==0 + + if (type!=='dir' && !excludeHidden) { + results.files.push(pushVal); + } + // should be the last statement in statHandler + if (!--pending){ + done() + } + } + } + } + + const onDirRead = function( + err, + list, + statOptions?: fs.StatSyncOptions + ) { + if (err) return callback(err); + + pending = list.length; + if (!pending){ + done(list); + return list + } + + var statHanOptions: FileReadOptions = {} + if( options.valuetizer ){ + statHanOptions.valuetizer = options.valuetizer + }else{ + statHanOptions.valuetizer = getValuetizerByOptions(options, dir) + } + + for (var file, i = 0, l = list.length; i < l; i++) { + var fname = list[i].toString(); + file = path.join(dir, fname); + //var buffile = Buffer.concat([bufdir, Buffer.from(path.sep), list[i]]); + + if(options.sync){ + var res = fs.statSync(file, statOptions); + getStatHandler(file, list[i], statHanOptions)(null, res) + }else{ + fs.stat(file, getStatHandler(file, list[i], statHanOptions)); + } + } + + return results + } + + const onStat = function( + err, + stat, + statOptions?: fs.StatSyncOptions + ) { + if (err) return callback(err); + if (stat && stat.mode === 17115) return done(); + + if(options.sync){ + const list = fs.readdirSync(dir) + return onDirRead(null, list, statOptions) + }else{ + fs.readdir(dir, + (err: NodeJS.ErrnoException, files: string[]) => onDirRead(err, files, statOptions) + ) + } + } + + if(options.sync){ + const stat = fs.statSync(dir); + return onStat(null, stat, statOptions) + }else{ + fs.stat(dir, onStat); + } +}; + + +/** + * find all files and subdirs in a directory (recursive) and pass them to callback fn + * + * @param {string} dir directory in which to recurse files or subdirs + * @param {boolean} combine whether to combine both subdirs and filepaths into one array (default false) + * @param {function(error, Object.<, Array.>)} callback fn to call when done + * @example + * dir.paths(__dirname, function (err, paths) { + * if (err) throw err; + * console.log('files:', paths.files); + * console.log('subdirs:', paths.dirs); + * }); + * dir.paths(__dirname, true, function (err, paths) { + * if (err) throw err; + * console.log('paths:', paths); + * }); + */ +exports.paths = function paths(dir, combine, callback) { + if (typeof combine === 'function') { + callback = combine; + combine = false; + } + + files(dir, 'all', function(err, results) { + if (err) return callback(err); + if (combine) { + callback(null, results.files.concat(results.dirs)); + } else { + callback(null, results); + } + }); +}; + + +/** + * find all subdirs (recursive) of a directory and pass them to callback fn + * + * @param {string} dir directory in which to find subdirs + * @param {string} type type of dir entry to recurse ('file' or 'dir', defaults to 'file') + * @param {function(error, )} callback fn to call when done + * @example + * dir.subdirs(__dirname, function (err, paths) { + * if (err) throw err; + * console.log('files:', paths.files); + * console.log('subdirs:', paths.dirs); + * }); + */ +exports.subdirs = function subdirs(dir, callback, type, options) { + options = options || {} + + const iCallback = function(err, subdirs) { + if (err) return callback(err); + + if(type=='combine'){ + subdirs = subdirs.files.concat(subdirs.dirs) + } + + if(options.sync)return subdirs + + callback(null, subdirs); + } + + const res = exports.files(dir, 'dir', iCallback, options) + + if(options && options.sync){ + return iCallback(null,res) + } +} + +function assign(c0, c1){ + for(var x in c1)c0[x] = c1[x] + return c0 +} + +function getValuetizerByOptions(options, dir){ + if(options.shortName){ + if( options.shortName=='relative' ){ + var dirBase = (options.basePath||dir) + var startPos = dirBase.length + if(dirBase.substring(dirBase.length-path.sep.length, dirBase.length)!=path.sep){ + startPos = startPos + path.sep.length + } + + return function(stat, shortName, longPath, isDir){ + return longPath.substring(startPos, longPath.length) + } + }else{ + return function(stat, shortName, longPath){ + return shortName + } + } + } + + return function(stat, shortName, longPath){ + return longPath + } +} diff --git a/ts/readfiles.ts b/ts/readfiles.ts new file mode 100644 index 0000000..c0ca049 --- /dev/null +++ b/ts/readfiles.ts @@ -0,0 +1,133 @@ +import * as fs from 'fs' +import * as path from 'path' + +/** + * merge two objects by extending target object with source object + * @param target object to merge + * @param source object to merge + * @param {Boolean} [modify] whether to modify the target + * @returns {Object} extended object + */ +function extend(target, source, modify?: boolean) { + var result = target ? modify ? target : extend({}, target, true) : {}; + if (!source) return result; + for (var key in source) { + if (source.hasOwnProperty(key) && source[key] !== undefined) { + result[key] = source[key]; + } + } + return result; +} + +/** + * determine if a string is contained within an array or matches a regular expression + * @param {String} str string to match + * @param {Array|Regex} match array or regular expression to match against + * @returns {Boolean} whether there is a match + */ +function matches(str, match) { + if (Array.isArray(match)) return match.indexOf(str) > -1; + return match.test(str); +} + +/** + * read files and call a function with the contents of each file + * @param {String} dir path of dir containing the files to be read + * @param {String} encoding file encoding (default is 'utf8') + * @param {Object} options options hash for encoding, recursive, and match/exclude + * @param {Function(error, string)} callback callback for each files content + * @param {Function(error)} complete fn to call when finished + */ +export function readFiles(dir, options, callback, complete) { + if (typeof options === 'function') { + complete = callback; + callback = options; + options = {}; + } + if (typeof options === 'string') options = { + encoding: options + }; + options = extend({ + recursive: true, + encoding: 'utf8', + doneOnErr: true + }, options); + var files = []; + + var done = function(err?: Error, _result?: any) { + if (typeof complete === 'function') { + if (err) return complete(err); + complete(null, files); + } + }; + + fs.readdir(dir, function(err, list) { + if (err) { + if (options.doneOnErr === true) { + if (err.code === 'EACCES') return done(); + return done(err); + } + } + var i = 0; + + if (options.reverse === true || + (typeof options.sort == 'string' && + (/reverse|desc/i).test(options.sort))) { + list = list.reverse(); + } else if (options.sort !== false) list = list.sort(); + + (function next() { + var filename = list[i++]; + if (!filename) return done(null, files); + + var file = path.join(dir, filename); + + fs.stat(file, function(err, stat) { + if (err && options.doneOnErr === true) return done(err); + if (stat && stat.isDirectory()) { + if (options.recursive) { + if (options.matchDir && !matches(filename, options.matchDir)) return next(); + if (options.excludeDir && matches(filename, options.excludeDir)) return next(); + readFiles(file, options, callback, function(err, sfiles) { + if (err && options.doneOnErr === true) return done(err); + files = files.concat(sfiles); + next(); + }); + } else next(); + } else if (stat && stat.isFile()) { + if (options.match && !matches(filename, options.match)) return next(); + if (options.exclude && matches(filename, options.exclude)) return next(); + if (options.filter && !options.filter(filename)) return next(); + + if (options.shortName){ + files.push(filename); + }else{ + files.push(file); + } + + fs.readFile(file, options.encoding, function(err, data) { + if (err) { + if (err.code === 'EACCES') return next(); + if (options.doneOnErr === true) { + return done(err); + } + } + if (callback.length > 3){ + if (options.shortName){ + callback(null, data, filename, next); + }else{ + callback(null, data, file, next); + } + }else{ + callback(null, data, next); + } + }); + } + else { + next(); + } + }); + })(); + + }); +} diff --git a/ts/readfilesstream.ts b/ts/readfilesstream.ts new file mode 100644 index 0000000..b53e838 --- /dev/null +++ b/ts/readfilesstream.ts @@ -0,0 +1,131 @@ +var fs = require('fs'), + mm = require('minimatch') + +import * as path from 'path' + +/** + * merge two objects by extending target object with source object + * @param target object to merge + * @param source object to merge + * @param {Boolean} [modify] whether to modify the target + * @returns {Object} extended object + */ +function extend(target, source, modify?: boolean) { + var result = target ? modify ? target : extend({}, target, true) : {}; + if (!source) return result; + for (var key in source) { + if (source.hasOwnProperty(key) && source[key] !== undefined) { + result[key] = source[key]; + } + } + return result; +} + +/** + * determine if a string is contained within an array or matches a regular expression + * @param {String} str string to match + * @param {Array|Regex} match array or regular expression to match against + * @returns {Boolean} whether there is a match + */ +function matches(str, match) { + if (Array.isArray(match)) { + var l = match.length; + for( var s=0; s < l; s++) { + if ( mm(str,match[s])) { + return true; + } + } + return false; + } + return match.test(str); +} + +/** + * read files and call a function with the contents of each file + * @param {String} dir path of dir containing the files to be read + * @param {String} encoding file encoding (default is 'utf8') + * @param {Object} options options hash for encoding, recursive, and match/exclude + * @param {Function(error, string)} callback callback for each files content + * @param {Function(error)} complete fn to call when finished + */ +export function readFilesStream(dir, options, callback, complete) { + if (typeof options === 'function') { + complete = callback; + callback = options; + options = {}; + } + if (typeof options === 'string') options = { + encoding: options + }; + options = extend({ + recursive: true, + encoding: 'utf8', + doneOnErr: true + }, options); + var files = []; + + var done = function(err?: Error, _result?: any) { + if (typeof complete === 'function') { + if (err) return complete(err); + complete(null, files); + } + }; + + fs.readdir(dir, function(err, list) { + if (err) { + if (options.doneOnErr === true) { + if (err.code === 'EACCES') return done(); + return done(err); + } + } + var i = 0; + + if (options.reverse === true || + (typeof options.sort == 'string' && + (/reverse|desc/i).test(options.sort))) { + list = list.reverse(); + } else if (options.sort !== false) list = list.sort(); + + (function next() { + var filename = list[i++]; + if (!filename) return done(null, files); + var file = path.join(dir, filename); + fs.stat(file, function(err, stat) { + if (err && options.doneOnErr === true) return done(err); + if (stat && stat.isDirectory()) { + if (options.recursive) { + if (options.matchDir && !matches(filename, options.matchDir)) return next(); + if (options.excludeDir && matches(filename, options.excludeDir)) return next(); + readFilesStream(file, options, callback, function(err, sfiles) { + if (err && options.doneOnErr === true) return done(err); + files = files.concat(sfiles); + next(); + }); + } else next(); + } else if (stat && stat.isFile()) { + if (options.match && !matches(filename, options.match)) return next(); + if (options.exclude && matches(filename, options.exclude)) return next(); + if (options.filter && !options.filter(filename)) return next(); + if (options.shortName) files.push(filename); + else files.push(file); + var stream = fs.createReadStream(file); + if (options.encoding !== null) { + stream.setEncoding(options.encoding); + } + stream.on('error',function(err) { + if (options.doneOnErr === true) return done(err); + next(); + }); + if (callback.length > 3) + if (options.shortName) callback(null, stream, filename, next); + else callback(null, stream, file, next); + else callback(null, stream, next); + } + else { + next(); + } + }); + })(); + + }); +} diff --git a/ts/tsconfig.json b/ts/tsconfig.json new file mode 100644 index 0000000..805964a --- /dev/null +++ b/ts/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "strict": false, + "strictPropertyInitialization": false, + "noUnusedLocals": true, + "rootDir": ".", + "removeComments": true, + "outDir": "../lib", + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "sourceMap": false, + "noEmitHelpers": false, + "noImplicitAny": false, + "declaration": true, + "skipLibCheck": true, + "stripInternal": true, + "noUnusedParameters": false, + "lib": ["dom", "es2015"], + "typeRoots": [ + "../node_modules/@types" + ] + }, + "exclude": [ + ], + "angularCompilerOptions": { + "skipMetadataEmit" : false, + "skipTemplateCodegen" : true + } +} \ No newline at end of file