UNPKG

node-readfiles

Version:

A lightweight Node.js module to recursively read files in a directory using ES6 Promises

813 lines 35.7 kB
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); }; define("src/build-filter", ["require", "exports"], function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildFilter = void 0; const buildFilter = (filtersParam) => { const filters = filtersParam instanceof Array ? filtersParam.slice() : [filtersParam]; const filterArray = []; if (filters.length === 0) return null; while (filters.length > 0) { const filter = filters.shift(); filterArray.push(`\\/?${filter .replace(/([./\\])/g, '\\$1') .replace(/(\*?)(\*)(?!\*)/g, (match, prefix) => { if (prefix === '*') { return match; } return '[^\\/]*'; }) .replace(/\?/g, '[^\\/]?') .replace(/\*\*/g, '.*') .replace(/([\-\+\|])/g, '\\$1')}`); } return new RegExp(`^${filterArray.join('|')}$`, 'i'); }; exports.buildFilter = buildFilter; }); define("src/build-filters.spec", ["require", "exports", "src/build-filter"], function (require, exports, build_filter_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); describe('buildFilter', () => { it('creates a filter RegExp given a filter string', () => { const result = (0, build_filter_1.buildFilter)('.'); expect(result).toEqual(/^\/?\.$/i); }); it('creates a filter RegExp given a wildcard filter string', () => { const result = (0, build_filter_1.buildFilter)('*'); expect(result).toEqual(/^\/?[^\/]*$/i); }); it('creates a filter RegExp given a wildcard filter string', () => { const result = (0, build_filter_1.buildFilter)('**'); expect(result).toEqual(/^\/?.*$/i); }); it('creates a filter RegExp given an array of filters', () => { const result = (0, build_filter_1.buildFilter)(['**/*123*', '**/abc.*']); expect(result).toEqual(/^\/?.*\/[^\/]*123[^\/]*|\/?.*\/abc\.[^\/]*$/i); }); }); }); define("src/consts", ["require", "exports"], function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FilenameFormat = void 0; var FilenameFormat; (function (FilenameFormat) { FilenameFormat[FilenameFormat["RELATIVE"] = 0] = "RELATIVE"; FilenameFormat[FilenameFormat["FULL_PATH"] = 1] = "FULL_PATH"; FilenameFormat[FilenameFormat["FILENAME"] = 2] = "FILENAME"; })(FilenameFormat = exports.FilenameFormat || (exports.FilenameFormat = {})); }); define("src/readfiles", ["require", "exports", "fs", "path", "src/build-filter", "src/consts"], function (require, exports, fs, path, build_filter_2, consts_1) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.readfiles = void 0; function readfiles(dir, optionsProp, callbackProp) { let callback = callbackProp; let options = optionsProp; if (typeof optionsProp === 'function') { callback = optionsProp; options = {}; } options = options || {}; callback = typeof callback === 'function' ? callback : () => { }; return new Promise((resolve, reject) => { const files = []; const subDirs = []; const filterRegExp = options.filter && (0, build_filter_2.buildFilter)(options.filter); const traverseDir = (dirPath, done) => { fs.readdir(dirPath, (err, fileListProp) => { let fileList = fileListProp; if (err) { if (options.rejectOnError !== false) { return reject(err); } return done(files); } if (options.reverse === true) { fileList = fileList.reverse(); } const next = () => { if (fileList.length === 0) { done(files); return; } const filename = fileList.shift(); const relFilename = path.join(subDirs.join('/'), filename); const fullPath = path.join(dirPath, filename); if (options.hidden !== true && /^\./.test(filename)) { return next(); } fs.stat(fullPath, (err, stat) => { if (err) { const result = callback(err, relFilename, null, stat); if (typeof result === 'function' && !err) { return result(next); } if (options.rejectOnError !== false) { return reject(err); } return next(); } if (stat.isDirectory()) { if (!isNaN(options.depth) && options.depth >= 0 && subDirs.length + 1 > options.depth) { return next(); } subDirs.push(filename); traverseDir(fullPath, () => { subDirs.pop(); next(); }); } else if (stat.isFile()) { if (filterRegExp && !filterRegExp.test(`/${relFilename}`)) { return next(); } let outputName = relFilename; if (options.filenameFormat === consts_1.FilenameFormat.FULL_PATH) { outputName = fullPath; } else if (options.filenameFormat === consts_1.FilenameFormat.FILENAME) { outputName = filename; } files.push(outputName); new Promise(resolve => { var _a; if (options.readContents === false) { return resolve(null); } fs.readFile(fullPath, (_a = options === null || options === void 0 ? void 0 : options.encoding) !== null && _a !== void 0 ? _a : 'utf8', (err, content) => { if (err) throw err; resolve(content); }); }) .then(content => { const result = callback(err, outputName, content, stat); if (typeof result === 'function' && !err) { return result(next); } options.async !== true && next(); }) .catch(err => { if (options.rejectOnError !== false) { return reject(err); } next(); }); } else { next(); } }); }; next(); }); }; traverseDir(dir, resolve); }); } exports.readfiles = readfiles; }); define("src/index", ["require", "exports", "src/consts", "src/readfiles", "src/readfiles"], function (require, exports, consts_2, readfiles_1, readfiles_2) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; __exportStar(consts_2, exports); __exportStar(readfiles_1, exports); Object.defineProperty(exports, "default", { enumerable: true, get: function () { return readfiles_2.readfiles; } }); }); define("test/fixtures", ["require", "exports"], function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.badDeepPathFixture = exports.deepPathFixture = exports.flatPathFixture = void 0; exports.flatPathFixture = { '/path/to/dir': { 'abc.txt': 'ABC', 'def.dat': 'DEF', 'test123.txt': '123', 'test456.dat': '456', }, }; exports.deepPathFixture = { '/path/to/dir': { '.system': 'SYSTEM', 'def.dat': 'DEF', 'abc.txt': 'ABC', 'abc123.txt': 'ABC123', subdir: { '.dot': 'DOT', 'test456.dat': '456', 'test789.txt': '789', 'test123.txt': '123', 'abc123.txt': 'ABC123', subsubdir: { '.hidden': 'HIDDEN', 'abc123.dat': 'ABC123', 'def456.dat': '456', }, }, otherdir: { '.other': 'DOT', 'test789.txt': '789', 'test123.txt': '123', subsubdir: { '.hidden': 'HIDDEN', 'abc123.txt': 'ABC123', 'def456.txt': '456', }, }, }, }; exports.badDeepPathFixture = { '/path/to/dir': { '.system': 'SYSTEM', 'def.dat': 'DEF', 'abc.txt': 'ABC', 'abc123.txt': 'ABC123', subdir: { '.dot': 'DOT', 'error-file.dat': false, 'test456.dat': '456', 'test789.txt': '789', 'test123.txt': '123', 'abc123.txt': 'ABC123', subsubdir: { '.hidden': 'HIDDEN', 'abc123.dat': 'ABC123', 'def456.dat': '456', }, }, otherdir: { '.other': 'DOT', 'error-file.dat': false, 'test789.txt': '789', 'test123.txt': '123', subsubdir: { '.hidden': 'HIDDEN', 'abc123.txt': 'ABC123', 'def456.txt': '456', }, }, }, }; }); define("test/fs-helper", ["require", "exports", "fs"], function (require, exports, fs) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mockFs = void 0; const fattenFixtures = (fixtureMap, rootPath = '') => { let flatMap = {}; if (rootPath) { flatMap[rootPath] = fixtureMap; } for (const path of Object.keys(fixtureMap).sort()) { if (typeof fixtureMap[path] !== 'object') { flatMap[`${rootPath}${path}`] = fixtureMap[path]; } else { flatMap = Object.assign({}, flatMap, fattenFixtures(fixtureMap[path], `${rootPath}${path}/`)); } } return flatMap; }; const mockFs = (fixture) => { jest.spyOn(fs, 'readdir').mockRestore(); jest.spyOn(fs, 'stat').mockRestore(); jest.spyOn(fs, 'readFile').mockRestore(); const pathsMap = fattenFixtures(fixture); jest.spyOn(fs, 'readdir').mockImplementation((readdirPath, readdirCb) => { if (!pathsMap[`${readdirPath}/`]) { return readdirCb(new Error(`ENOENT, no such file or directory '${readdirPath}'`), null); } jest.spyOn(fs, 'stat').mockImplementation((statPath, statCb) => { if (!pathsMap[statPath] && !pathsMap[`${statPath}/`]) { return statCb(new Error(`ENOENT, no such file or directory, stat '${statPath}'`), null); } statCb(null, { isDirectory: () => typeof pathsMap[`${statPath}/`] === 'object', isFile: () => typeof pathsMap[statPath] === 'string', }); }); jest.spyOn(fs, 'readFile').mockImplementation(((readFilePath, encoding, readFileCb) => { if (!pathsMap[readFilePath]) { return readFileCb(new Error(`ENOENT, no such file or directory '${readFilePath}'`), null); } readFileCb(null, pathsMap[readFilePath]); })); readdirCb(null, Object.keys(pathsMap[`${readdirPath}/`]).sort()); }); }; exports.mockFs = mockFs; }); define("src/readfiles.spec", ["require", "exports", "src/index", "test/fixtures", "test/fs-helper", "src/consts"], function (require, exports, index_1, fixtures_1, fs_helper_1, consts_3) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); describe('readfiles', () => { describe('defaults', () => { beforeEach(() => { jest.useRealTimers(); try { jest.useFakeTimers({ legacyFakeTimers: true }); } catch (e) { jest.useFakeTimers(); } (0, fs_helper_1.mockFs)(fixtures_1.flatPathFixture); }); afterEach(() => { jest.useRealTimers(); }); it('should return the list of files and their contents', done => { const files = ['abc.txt', 'def.dat', 'test123.txt', 'test456.dat']; const contents = ['ABC', 'DEF', '123', '456']; (0, index_1.default)('/path/to/dir', (err, filename, content) => { expect(filename).toEqual(files.shift()); expect(content).toEqual(contents.shift()); if (files.length === 0) done(); }).catch(err => { done(err); }); }); it('should throw an error on a file', done => { (0, fs_helper_1.mockFs)({ '/path/to/dir': { 'badfile.txt': false, }, }); (0, index_1.default)('/path/to/dir', (err, filename, content) => { expect(content).toBeNull(); expect(err.message).toEqual("ENOENT, no such file or directory, stat '/path/to/dir/badfile.txt'"); }).catch(err => { expect(err.message).toEqual("ENOENT, no such file or directory, stat '/path/to/dir/badfile.txt'"); done(); }); }); it('should resolve the promise when finished traversing all files', done => { (0, index_1.default)('/path/to/dir') .then(files => { expect(files).toHaveLength(4); done(); }) .catch(function (err) { done(err); }); }); it('should call the done callback with an error on a path', function (done) { (0, fs_helper_1.mockFs)({}); (0, index_1.default)('/fake/invalid/dir').catch(function (err) { expect(err.message).toEqual("ENOENT, no such file or directory '/fake/invalid/dir'"); done(); }); }); it('should wait for an asynchronous callback when returning a function', function (done) { let count = 0; const expectFiles = ['abc.txt', 'def.dat', 'test123.txt', 'test456.dat']; (0, index_1.default)('/path/to/dir', function (err, filename) { return function (next) { expect(filename).toEqual(expectFiles[count++]); setTimeout(function () { next(); }, 1000); jest.advanceTimersToNextTimer(); }; }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); }); describe('options', () => { beforeEach(() => { (0, fs_helper_1.mockFs)(fixtures_1.deepPathFixture); }); it("callback returns the list of files in reverse order when 'reverse' is true", done => { (0, index_1.default)('/path/to/dir', { reverse: true }) .then(files => { expect(files).toEqual([ 'subdir/test789.txt', 'subdir/test456.dat', 'subdir/test123.txt', 'subdir/subsubdir/def456.dat', 'subdir/subsubdir/abc123.dat', 'subdir/abc123.txt', 'otherdir/test789.txt', 'otherdir/test123.txt', 'otherdir/subsubdir/def456.txt', 'otherdir/subsubdir/abc123.txt', 'def.dat', 'abc123.txt', 'abc.txt', ]); done(); }) .catch(function (err) { done(err); }); }); it("callback returns the full path of the files when 'filenameFormat' is 'readfiles.FULL_PATH'", done => { let count = 0; const expectFiles = [ '/path/to/dir/abc.txt', '/path/to/dir/abc123.txt', '/path/to/dir/def.dat', '/path/to/dir/otherdir/subsubdir/abc123.txt', '/path/to/dir/otherdir/subsubdir/def456.txt', '/path/to/dir/otherdir/test123.txt', '/path/to/dir/otherdir/test789.txt', '/path/to/dir/subdir/abc123.txt', '/path/to/dir/subdir/subsubdir/abc123.dat', '/path/to/dir/subdir/subsubdir/def456.dat', '/path/to/dir/subdir/test123.txt', '/path/to/dir/subdir/test456.dat', '/path/to/dir/subdir/test789.txt', ]; (0, index_1.default)('/path/to/dir', { filenameFormat: consts_3.FilenameFormat.FULL_PATH }, (err, filename) => { expect(filename).toEqual(expectFiles[count++]); }) .then(files => { expect(files).toEqual(expectFiles); done(); }) .catch(err => { done(err); }); }); it("callback returns the relative path of the files when 'filenameFormat' is 'readfiles.RELATIVE'", done => { const count = 0; const expectFiles = [ 'abc.txt', 'abc123.txt', 'def.dat', 'otherdir/subsubdir/abc123.txt', 'otherdir/subsubdir/def456.txt', 'otherdir/test123.txt', 'otherdir/test789.txt', 'subdir/abc123.txt', 'subdir/subsubdir/abc123.dat', 'subdir/subsubdir/def456.dat', 'subdir/test123.txt', 'subdir/test456.dat', 'subdir/test789.txt', ]; (0, index_1.default)('/path/to/dir', { filenameFormat: consts_3.FilenameFormat.RELATIVE }, (err, filename) => { expect(expectFiles).toContain(filename); }) .then(files => { expect(files).toEqual(expectFiles); done(); }) .catch(err => { done(err); }); }); it("callback returns only the filename of the file when 'filenameFormat' is 'readfiles.FILENAME'", done => { const count = 0; const expectFiles = [ 'abc.txt', 'abc123.txt', 'def.dat', 'abc123.txt', 'def456.txt', 'test123.txt', 'test789.txt', 'abc123.txt', 'abc123.dat', 'def456.dat', 'test123.txt', 'test456.dat', 'test789.txt', ]; (0, index_1.default)('/path/to/dir', { filenameFormat: consts_3.FilenameFormat.FILENAME }, (err, filename) => { expect(expectFiles).toContain(filename); }) .then(files => { expect(files).toEqual(expectFiles); done(); }) .catch(err => { done(err); }); }); it("does not stop reading files when one file throws an error and 'rejectOnError' is false", done => { let fileCount = 0; (0, fs_helper_1.mockFs)(fixtures_1.badDeepPathFixture); (0, index_1.default)('/path/to/dir', { rejectOnError: false }, err => { fileCount++; }) .then(files => { expect(fileCount).toEqual(15); expect(files.length).toEqual(13); done(); }) .catch(err => { done(err); }); }); it("callback does not return the file contents when 'readContents' is false", done => { let fileCount = 0; (0, index_1.default)('/path/to/dir', { readContents: false }, (err, filename, contents) => { expect(contents).toBeNull(); fileCount++; }) .then(files => { expect(fileCount).toEqual(13); expect(files.length).toEqual(13); done(); }) .catch(err => { done(err); }); }); it("callback returns file contents encoded in the specified 'encoding' type", done => { const expectFiles = { 'abc.txt': 'ABC', 'abc123.txt': 'ABC123', 'def.dat': 'DEF', 'otherdir/subsubdir/abc123.txt': 'ABC123', 'otherdir/subsubdir/def456.txt': '456', 'otherdir/symlink.dat': '123', 'otherdir/test123.txt': '123', 'otherdir/test789.txt': '789', 'subdir/abc123.txt': 'ABC123', 'subdir/subsubdir/abc123.dat': 'ABC123', 'subdir/subsubdir/def456.dat': '456', 'subdir/test123.txt': '123', 'subdir/test456.dat': '456', 'subdir/test789.txt': '789', }; (0, index_1.default)('/path/to/dir', { encoding: null }, (err, filename, contents) => { expect(contents).toEqual(expectFiles[filename]); }) .then(files => { expect(files.length).toEqual(13); done(); }) .catch(err => { done(err); }); }); it("traverses the directory tree limiting to specified 'depth'", done => { let count = 0; const expectFiles = [ 'abc.txt', 'abc123.txt', 'def.dat', 'otherdir/test123.txt', 'otherdir/test789.txt', 'subdir/abc123.txt', 'subdir/test123.txt', 'subdir/test456.dat', 'subdir/test789.txt', ]; (0, index_1.default)('/path/to/dir', { depth: 1 }, (err, filename) => { expect(filename).toEqual(expectFiles[count++]); }) .then(files => { expect(files).toEqual(expectFiles); done(); }) .catch(err => { done(err); }); }); it("callback returns all files including hidden files when 'hidden' is true", done => { let count = 0; const expectFiles = [ '.system', 'abc.txt', 'abc123.txt', 'def.dat', 'otherdir/.other', 'otherdir/subsubdir/.hidden', 'otherdir/subsubdir/abc123.txt', 'otherdir/subsubdir/def456.txt', 'otherdir/test123.txt', 'otherdir/test789.txt', 'subdir/.dot', 'subdir/abc123.txt', 'subdir/subsubdir/.hidden', 'subdir/subsubdir/abc123.dat', 'subdir/subsubdir/def456.dat', 'subdir/test123.txt', 'subdir/test456.dat', 'subdir/test789.txt', ]; (0, index_1.default)('/path/to/dir', { hidden: true }, (err, filename) => { expect(filename).toEqual(expectFiles[count++]); }) .then(files => { expect(files).toEqual(expectFiles); done(); }) .catch(err => { done(err); }); }); }); describe('filters', function () { it("callback returns all files in the given directory when the 'filter' option is equal '*'", function (done) { const expectFiles = ['abc.txt', 'abc123.txt', 'def.dat']; (0, index_1.default)('/path/to/dir', { filter: '*', }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); it("callback returns all files in the given directory recursively when the 'filter' option is equal '**'", function (done) { const expectFiles = [ 'abc.txt', 'abc123.txt', 'def.dat', 'otherdir/subsubdir/abc123.txt', 'otherdir/subsubdir/def456.txt', 'otherdir/test123.txt', 'otherdir/test789.txt', 'subdir/abc123.txt', 'subdir/subsubdir/abc123.dat', 'subdir/subsubdir/def456.dat', 'subdir/test123.txt', 'subdir/test456.dat', 'subdir/test789.txt', ]; (0, index_1.default)('/path/to/dir', { filter: '**', }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); it("callback returns all \"txt\" files in the given directory when the 'filter' option is equal '*.txt'", function (done) { const expectFiles = ['abc.txt', 'abc123.txt']; (0, index_1.default)('/path/to/dir', { filter: '*.txt', }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); it("callback returns all \"txt\" files in the given directory recursively when the 'filter' option is equal '**/*.txt'", function (done) { const expectFiles = [ 'abc.txt', 'abc123.txt', 'otherdir/subsubdir/abc123.txt', 'otherdir/subsubdir/def456.txt', 'otherdir/test123.txt', 'otherdir/test789.txt', 'subdir/abc123.txt', 'subdir/test123.txt', 'subdir/test789.txt', ]; (0, index_1.default)('/path/to/dir', { filter: '**/*.txt', }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); it("callback returns all files that match \"abc.txt\" in the given directory recursively when the 'filter' option is equal '**/abc123.txt'", function (done) { const expectFiles = ['abc123.txt', 'otherdir/subsubdir/abc123.txt', 'subdir/abc123.txt']; (0, index_1.default)('/path/to/dir', { filter: '**/abc123.txt', }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); it("callback returns all files that match \"abc123.txt\" in the given directory when the 'filter' option is equal 'abc123.txt'", function (done) { const expectFiles = ['abc123.txt']; (0, index_1.default)('/path/to/dir', { filter: 'abc123.txt', }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); it("callback returns all files in all sub-directory of the given directory when the 'filter' option is equal '*/*'", function (done) { const expectFiles = [ 'abc.txt', 'abc123.txt', 'def.dat', 'otherdir/test123.txt', 'otherdir/test789.txt', 'subdir/abc123.txt', 'subdir/test123.txt', 'subdir/test456.dat', 'subdir/test789.txt', ]; (0, index_1.default)('/path/to/dir', { filter: '*/*', }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); it("callback returns all files where the extension matches \"t?t\" in the given directory when the 'filter' option is equal '*.??t'", function (done) { const expectFiles = ['abc.txt', 'abc123.txt']; (0, index_1.default)('/path/to/dir', { filter: '*.t?t', }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); it("callback returns all files where the extension matches \"t?t\" in the given directory recursively when the 'filter' option is equal '**/*.??t'", function (done) { const expectFiles = [ 'abc.txt', 'abc123.txt', 'otherdir/subsubdir/abc123.txt', 'otherdir/subsubdir/def456.txt', 'otherdir/test123.txt', 'otherdir/test789.txt', 'subdir/abc123.txt', 'subdir/test123.txt', 'subdir/test789.txt', ]; (0, index_1.default)('/path/to/dir', { filter: '**/*.t??', }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); it("callback returns all files that match the array of filters in the given directory when the 'filter' option is equal ['*123*', 'abc.*'] ", function (done) { const expectFiles = [ 'abc.txt', 'abc123.txt', 'otherdir/subsubdir/abc123.txt', 'otherdir/test123.txt', 'subdir/abc123.txt', 'subdir/subsubdir/abc123.dat', 'subdir/test123.txt', ]; (0, index_1.default)('/path/to/dir', { filter: ['**/*123*', '**/abc.*'], }) .then(function (files) { expect(files).toEqual(expectFiles); done(); }) .catch(function (err) { done(err); }); }); }); }); }); //# sourceMappingURL=readfiles.js.map