UNPKG

3.21 kBJavaScriptView Raw
1'use strict';
2
3const glob = require('glob');
4const async = require('async');
5const fs = require('fs');
6const combined = require('combined-stream2');
7const _ = require('lodash/fp');
8
9const _defaults = {
10 glob: {},
11 stream: false,
12};
13
14const _once = function(fn) {
15 let value;
16 let called = false;
17 return function() {
18 if (called) {
19 return value;
20 }
21
22 value = fn.apply(this, arguments);
23 called = true;
24
25 return value;
26 };
27};
28
29const _promiseGlob = function(patterns, options) {
30 patterns = Array.isArray(patterns) ? patterns : [patterns];
31 return new Promise((resolve, reject) => {
32 async.map(patterns, function(pattern, done) {
33 glob(pattern, options, done);
34 }, function(err, results) {
35 if (err) {
36 reject(err);
37 } else {
38 resolve(_.uniq(_.flatten(results)));
39 }
40 });
41 });
42};
43
44const _createStream = function(files) {
45 return new Promise((resolve, reject) => {
46 async.map(files, (file, done) => {
47 let callback = _once(done);
48
49 fs.stat(file, (err, stats) => {
50 if (err) {
51 callback(err);
52 return;
53 }
54
55 if (stats.isFile()) {
56 let stream = fs.createReadStream(file);
57
58 stream.on('open', () => {
59 callback(null, stream);
60 });
61
62 stream.on('error', (err) => {
63 callback(err);
64 });
65 } else {
66 callback(new Error('Not a file: ' + file));
67 }
68 });
69 }, (err, streams) => {
70 if (err) {
71 reject(err);
72 return;
73 }
74
75 let stream = streams.reduce((combinedStream, fileStream) => {
76 combinedStream.append(fileStream);
77 return combinedStream;
78 }, combined.create());
79
80 resolve(stream);
81 });
82 });
83};
84
85const _toString = function(asStream) {
86 return function(stream) {
87 if (asStream) {
88 return stream;
89 }
90
91 return new Promise((resolve, reject) => {
92 let str = '';
93
94 stream.on('data', (buffer) => {
95 str += buffer;
96 });
97
98 stream.on('error', (err) => {
99 reject(err);
100 });
101
102 stream.on('end', () => {
103 resolve(str);
104 });
105 });
106 };
107};
108
109/**
110 * Find files using glob pattern(s) get a string or stream of
111 * the combined files' content.
112 * @param {String|String[]} patterns - One or more glob patterns.
113 * @param {Object} [options] - Options object. Optional.
114 * @param {Boolean} [options.stream=false] - Get results as a stream. Optional.
115 * @param {Object} [options.glob={}] - Options to send to glob.
116 * @param {Function} [callback]
117 * @returns {Promise} Returns a promise if no callback is provided.
118 * @see {@link https://www.npmjs.com/package/glob}
119 */
120module.exports = function(patterns, options, callback) {
121 let settings = _.defaultsDeep(_defaults, options);
122 let promise = _promiseGlob(patterns, settings.glob)
123 .then(_createStream)
124 .then(_toString(settings.stream))
125 .then((result) => {
126 if (callback) {
127 callback(null, result);
128 } else {
129 return result;
130 }
131 })
132 .catch((err) => {
133 if (callback) {
134 callback(err);
135 } else {
136 throw err;
137 }
138 });
139
140 if (!callback) {
141 return promise;
142 }
143};