Skip to content

Commit 3fd8df5

Browse files
committed
integrate amazing-graceful-fs with graceful-fs
all credit goes to ben https://github.com/bnoordhuis/amazing-graceful-fs and nodejs/node#2026 formatting was modified to match
1 parent 460834a commit 3fd8df5

5 files changed

Lines changed: 189 additions & 136 deletions

File tree

fs.js

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1-
// eeeeeevvvvviiiiiiillllll
2-
// more evil than monkey-patching the native builtin?
3-
// Not sure.
4-
5-
var mod = require("module")
6-
var pre = '(function (exports, require, module, __filename, __dirname) { '
7-
var post = '});'
8-
var src = pre + process.binding('natives').fs + post
9-
var vm = require('vm')
10-
var fn = vm.runInThisContext(src)
11-
fn(exports, require, module, __filename, __dirname)
1+
'use strict'
2+
3+
var fs = require('fs')
4+
5+
module.exports = clone(fs)
6+
7+
function clone (obj) {
8+
if (obj === null || typeof obj !== 'object')
9+
return obj
10+
11+
if ('__proto__' in obj)
12+
var copy = { __proto__: obj.__proto__ }
13+
else
14+
var copy = Object.create(null)
15+
16+
Object.getOwnPropertyNames(obj).forEach(function (key) {
17+
Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key))
18+
})
19+
20+
return copy
21+
}

graceful-fs.js

Lines changed: 108 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,148 @@
1-
// Monkey-patching the fs module.
2-
// It's ugly, but there is simply no other way to do this.
3-
var fs = module.exports = require('./fs.js')
4-
5-
var assert = require('assert')
1+
var fs = require('fs')
2+
module.exports = require('./fs.js')
63

74
// fix up some busted stuff, mostly on windows and old nodes
85
require('./polyfills.js')
96

10-
var util = require('util')
7+
module.exports.FileReadStream = ReadStream; // Legacy name.
8+
module.exports.FileWriteStream = WriteStream; // Legacy name.
9+
module.exports.ReadStream = ReadStream
10+
module.exports.WriteStream = WriteStream
11+
module.exports.close = close
12+
module.exports.closeSync = closeSync
13+
module.exports.createReadStream = createReadStream
14+
module.exports.createWriteStream = createWriteStream
15+
module.exports.open = open
16+
module.exports.readFile = readFile
17+
module.exports.readdir = readdir
1118

12-
function noop () {}
19+
ReadStream.prototype = Object.create(fs.ReadStream.prototype)
20+
ReadStream.prototype.open = ReadStream$open
1321

14-
var debug = noop
15-
if (util.debuglog)
16-
debug = util.debuglog('gfs')
17-
else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || ''))
18-
debug = function() {
19-
var m = util.format.apply(util, arguments)
20-
m = 'GFS: ' + m.split(/\n/).join('\nGFS: ')
21-
console.error(m)
22-
}
22+
WriteStream.prototype = Object.create(fs.WriteStream.prototype)
23+
WriteStream.prototype.open = WriteStream$open
2324

24-
if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) {
25-
process.on('exit', function() {
26-
debug('fds', fds)
27-
debug(queue)
28-
assert.equal(queue.length, 0)
29-
})
30-
}
31-
32-
33-
var originalOpen = fs.open
34-
fs.open = open
25+
var queue = []
3526

36-
function open(path, flags, mode, cb) {
37-
if (typeof mode === "function") cb = mode, mode = null
38-
if (typeof cb !== "function") cb = noop
39-
new OpenReq(path, flags, mode, cb)
27+
function ReadStream (path, options) {
28+
if (this instanceof ReadStream)
29+
return fs.ReadStream.apply(this, arguments), this
30+
else
31+
return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
4032
}
4133

42-
function OpenReq(path, flags, mode, cb) {
43-
this.path = path
44-
this.flags = flags
45-
this.mode = mode
46-
this.cb = cb
47-
Req.call(this)
34+
function ReadStream$open () {
35+
var that = this
36+
open(that.path, that.flags, that.mode, function (err, fd) {
37+
if (err) {
38+
if (that.autoClose)
39+
that.destroy()
40+
41+
that.emit('error', err)
42+
} else {
43+
that.fd = fd
44+
that.emit('open', fd)
45+
that.read()
46+
}
47+
})
4848
}
4949

50-
util.inherits(OpenReq, Req)
51-
52-
OpenReq.prototype.process = function() {
53-
originalOpen.call(fs, this.path, this.flags, this.mode, this.done)
50+
function WriteStream (path, options) {
51+
if (this instanceof WriteStream)
52+
return fs.WriteStream.apply(this, arguments), this
53+
else
54+
return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
5455
}
5556

56-
var fds = {}
57-
OpenReq.prototype.done = function(er, fd) {
58-
debug('open done', er, fd)
59-
if (fd)
60-
fds['fd' + fd] = this.path
61-
Req.prototype.done.call(this, er, fd)
57+
function WriteStream$open () {
58+
var that = this
59+
open(that.path, that.flags, that.mode, function (err, fd) {
60+
if (err) {
61+
that.destroy()
62+
that.emit('error', err)
63+
} else {
64+
that.fd = fd
65+
that.emit('open', fd)
66+
}
67+
})
6268
}
6369

70+
function close (fd, cb) {
71+
return fs.close(fd, function (err) {
72+
if (!err)
73+
retry()
6474

65-
var originalReaddir = fs.readdir
66-
fs.readdir = readdir
67-
68-
function readdir(path, cb) {
69-
if (typeof cb !== "function") cb = noop
70-
new ReaddirReq(path, cb)
75+
if (typeof cb === 'function')
76+
cb.apply(this, arguments)
77+
})
7178
}
7279

73-
function ReaddirReq(path, cb) {
74-
this.path = path
75-
this.cb = cb
76-
Req.call(this)
80+
function closeSync () {
81+
// Note that graceful-fs also retries when fs.closeSync() fails.
82+
// Looks like a bug to me, although it's probably a harmless one.
83+
var rval = fs.closeSync.apply(fs, arguments)
84+
retry()
85+
return rval
7786
}
7887

79-
util.inherits(ReaddirReq, Req)
80-
81-
ReaddirReq.prototype.process = function() {
82-
originalReaddir.call(fs, this.path, this.done)
88+
function createReadStream (path, options) {
89+
return new ReadStream(path, options)
8390
}
8491

85-
ReaddirReq.prototype.done = function(er, files) {
86-
if (files && files.sort)
87-
files = files.sort()
88-
Req.prototype.done.call(this, er, files)
89-
onclose()
92+
function createWriteStream (path, options) {
93+
return new WriteStream(path, options)
9094
}
9195

96+
function open (path, flags, mode, cb) {
97+
if (typeof mode === 'function')
98+
cb = mode, mode = null
9299

93-
var originalClose = fs.close
94-
fs.close = close
100+
return go(path, flags, mode, cb)
95101

96-
function close (fd, cb) {
97-
debug('close', fd)
98-
if (typeof cb !== "function") cb = noop
99-
delete fds['fd' + fd]
100-
originalClose.call(fs, fd, function(er) {
101-
onclose()
102-
cb(er)
103-
})
102+
function go (path, flags, mode, cb) {
103+
return fs.open(path, flags, mode, function (err, fd) {
104+
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
105+
queue.push([go, [path, flags, mode, cb]])
106+
else if (typeof cb === 'function')
107+
cb.apply(this, arguments)
108+
})
109+
}
104110
}
105111

112+
function readFile (path, options, cb) {
113+
if (typeof options === 'function')
114+
cb = options, options = null
106115

107-
var originalCloseSync = fs.closeSync
108-
fs.closeSync = closeSync
116+
return go(path, options, cb)
109117

110-
function closeSync (fd) {
111-
try {
112-
return originalCloseSync(fd)
113-
} finally {
114-
onclose()
118+
function go (path, flags, mode, cb) {
119+
return fs.readFile(path, options, function (err, fd) {
120+
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
121+
queue.push([go, [path, options, cb]])
122+
else if (typeof cb === 'function')
123+
cb.apply(this, arguments)
124+
})
115125
}
116126
}
117127

128+
function readdir (path, cb) {
129+
return go(path, cb)
118130

119-
// Req class
120-
function Req () {
121-
// start processing
122-
this.done = this.done.bind(this)
123-
this.failures = 0
124-
this.process()
125-
}
126-
127-
Req.prototype.done = function (er, result) {
128-
var tryAgain = false
129-
if (er) {
130-
var code = er.code
131-
var tryAgain = code === "EMFILE" || code === "ENFILE"
132-
if (process.platform === "win32")
133-
tryAgain = tryAgain || code === "OK"
134-
}
131+
function go () {
132+
return fs.readdir(path, function (err, files) {
133+
if (files && files.sort)
134+
files.sort(); // Backwards compatibility with graceful-fs.
135135

136-
if (tryAgain) {
137-
this.failures ++
138-
enqueue(this)
139-
} else {
140-
var cb = this.cb
141-
cb(er, result)
136+
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
137+
queue.push([go, [path, cb]])
138+
else if (typeof cb === 'function')
139+
cb.apply(this, arguments)
140+
})
142141
}
143142
}
144143

145-
var queue = []
146-
147-
function enqueue(req) {
148-
queue.push(req)
149-
debug('enqueue %d %s', queue.length, req.constructor.name, req)
150-
}
151-
152-
function onclose() {
153-
var req = queue.shift()
154-
if (req) {
155-
debug('process', req.constructor.name, req)
156-
req.process()
157-
}
144+
function retry () {
145+
var elem = queue.shift()
146+
if (elem)
147+
elem[0].apply(null, elem[1])
158148
}

test/open.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
var test = require('tap').test
2-
var fs = require('../graceful-fs.js')
3-
4-
test('graceful fs is monkeypatched fs', function (t) {
5-
t.equal(fs, require('../fs.js'))
6-
t.end()
7-
})
2+
var fs = require('../')
83

94
test('open an existing file works', function (t) {
105
var fd = fs.openSync(__filename, 'r')

test/read-write-stream.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict'
2+
3+
var fs = require('../')
4+
var rimraf = require('rimraf')
5+
var mkdirp = require('mkdirp')
6+
var test = require('tap').test
7+
var p = require('path').resolve(__dirname, 'files')
8+
9+
process.chdir(__dirname)
10+
11+
// Make sure to reserve the stderr fd
12+
process.stderr.write('')
13+
14+
var num = 4097
15+
var paths = new Array(num)
16+
17+
test('write files', function (t) {
18+
rimraf.sync(p)
19+
mkdirp.sync(p)
20+
21+
var done = 0
22+
for (var i = 0; i < num; ++i) {
23+
paths[i] = 'files/file-' + i
24+
var stream = fs.createWriteStream(paths[i])
25+
stream.on('end', function () {
26+
++done
27+
if (done === num) {
28+
t.pass('success')
29+
t.end()
30+
}
31+
})
32+
stream.write('content')
33+
stream.end()
34+
}
35+
36+
t.end()
37+
})
38+
39+
test('read files', function (t) {
40+
// now read them
41+
var done = 0
42+
for (var i = 0; i < num; ++i) {
43+
var stream = fs.createReadStream(paths[i])
44+
stream.on('data', function (data) {})
45+
stream.on('end', function () {
46+
++done
47+
if (done === num) {
48+
t.pass('success')
49+
t.end()
50+
}
51+
})
52+
}
53+
})
54+
55+
test('cleanup', function (t) {
56+
rimraf.sync(p)
57+
t.end()
58+
})

test/readdir-sort.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
var test = require("tap").test
2-
var fs = require("../fs.js")
2+
var fs = require("fs")
33

44
var readdir = fs.readdir
55
fs.readdir = function(path, cb) {

0 commit comments

Comments
 (0)