UNPKG

3.28 kBJavaScriptView Raw
1var EventEmitter = require('events').EventEmitter
2var watchDirs = require('./utils').watchDirs
3var readdir = require('recursive-readdir')
4var async = require('async-collection')
5var assert = require('assert')
6var path = require('path')
7var fs = require('fs')
8
9var READ_CONCURRENCY = 3
10
11// This is a data structure to keep track of a single directory, and expose
12// all file names + sizes inside an object. It emits two events:
13//
14// .on('error') - Emitted when an error occurs.
15// .on('change') - Emitted when files have changed, including init.
16// .on('progress') - Emitted when progress has been made reading.
17
18module.exports = Tracker
19
20function Tracker () {
21 if (!(this instanceof Tracker)) return new Tracker()
22 this._unwatch = null
23 this.watching = false
24 this.count = 0
25 this.files = {}
26 EventEmitter.call(this)
27}
28Tracker.prototype = Object.create(EventEmitter.prototype)
29
30Tracker.prototype.start = function (basedir, dirnames, opts) {
31 var list = [] // Initial list all files between all directories.
32 var count = 0 // Amount of items done so far.
33 opts = opts || {}
34 var watch = opts.watch === undefined ? false : opts.watch
35
36 assert.strictEqual(typeof basedir, 'string', 'lib/track-dir: basedir should be type string')
37 assert.ok(Array.isArray(dirnames), 'lib/track-dir: basedir should be an Array')
38 assert.strictEqual(this.watching, false, 'lib/track-dir: already watching a set of directories')
39
40 // Read out all files in all directories once.
41 async.mapLimit(dirnames, 1, init, function (err) {
42 if (err) return self.emit('error', err)
43
44 async.mapLimit(list, READ_CONCURRENCY, wrap, function (err) {
45 if (err) return self.emit('error', err)
46 self.emit('progress', 100)
47 self.emit('change')
48 })
49
50 function wrap (filename, done) {
51 readFile(filename, function (err) {
52 if (err) return done(err)
53 self.emit('progress', Math.trunc((count / list.length) * 100))
54 done()
55 })
56 }
57 })
58
59 var self = this
60
61 if (watch) {
62 var unwatch = watchDirs(basedir, dirnames, function (filePath) {
63 readFile(filePath, function (err) {
64 if (err) return self.emit('error', err)
65 self.emit('change')
66 })
67 })
68
69 this.watching = true
70 this._unwatch = unwatch.bind(unwatch)
71 }
72
73 // Run only once at the start of the pass.
74 function init (dirname, done) {
75 dirname = path.join(basedir, dirname)
76 fs.access(dirname, function (err) {
77 if (err) return done()
78 readdir(dirname, function (err, _list) {
79 if (err) return done(err)
80 list = list.concat(_list)
81 done()
82 })
83 })
84 }
85
86 // Read a file, and add it to this.files {}
87 function readFile (filePath, done) {
88 fs.stat(filePath, function (err, stat) {
89 if (err) {
90 delete self.files[filePath]
91 done()
92 } else {
93 self.files[filePath] = stat
94 done()
95 }
96 })
97 }
98}
99
100Tracker.prototype.unwatch = function () {
101 if (!this.watching) return
102 this._unwatch()
103}
104
105Tracker.prototype.size = function () {
106 var self = this
107 return Object.keys(this.files).reduce(function (size, filename) {
108 var file = self.files[filename]
109 return size + file.size
110 }, 0)
111}
112
113Tracker.prototype.list = function () {
114 return Object.keys(this.files)
115}