diff --git a/README.md b/README.md index 6dbe8cd..6623d7b 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,15 @@ jsonfile.readFile(file, function(err, obj) { }) ``` +You can also use this method with promises. The readFile() method will return a promise if you do not pass a callback function. + +```js +var jsonfile = require('jsonfile') +var file = '/tmp/data.json' +jsonfile.readFile(file) + .then(obj => console.dir(obj))) + .catch(error => console.log(error)); +``` ### readFileSync(filename, [options]) @@ -70,6 +79,21 @@ jsonfile.writeFile(file, obj, function (err) { console.error(err) }) ``` +Or use with promises as follows: + +```js +var jsonfile = require('jsonfile') + +var file = '/tmp/data.json' +var obj = {name: 'JP'} + +jsonfile.writeFile(file, obj) + .then(res => { + console.log("Write complete"); + }) + .catch(error => console.log(error)); +``` + **formatting with spaces:** diff --git a/index.js b/index.js index d1e5827..57e13d8 100644 --- a/index.js +++ b/index.js @@ -4,15 +4,16 @@ try { } catch (_) { _fs = require('fs') } +const universalify = require('universalify') -function readFile (file, options, callback) { +function readFileWithCallback (file, options, callback) { if (callback == null) { callback = options options = {} } if (typeof options === 'string') { - options = {encoding: options} + options = { encoding: options } } options = options || {} @@ -44,10 +45,12 @@ function readFile (file, options, callback) { }) } +const readFile = universalify.fromCallback(readFileWithCallback) + function readFileSync (file, options) { options = options || {} if (typeof options === 'string') { - options = {encoding: options} + options = { encoding: options } } var fs = options.fs || _fs @@ -88,7 +91,7 @@ function stringify (obj, options) { return str.replace(/\n/g, EOL) + EOL } -function writeFile (file, obj, options, callback) { +function writeFileWithCallback (file, obj, options, callback) { if (callback == null) { callback = options options = {} @@ -108,6 +111,8 @@ function writeFile (file, obj, options, callback) { fs.writeFile(file, str, options, callback) } +const writeFile = universalify.fromCallback(writeFileWithCallback) + function writeFileSync (file, obj, options) { options = options || {} var fs = options.fs || _fs diff --git a/package.json b/package.json index 29e783f..8159929 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ ], "author": "JP Richardson ", "license": "MIT", - "dependencies": {}, + "dependencies": { + "universalify": "^0.1.2" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" }, diff --git a/test/read-file-sync.test.js b/test/read-file-sync.test.js index 5c53c82..2e20150 100644 --- a/test/read-file-sync.test.js +++ b/test/read-file-sync.test.js @@ -23,7 +23,7 @@ describe('+ readFileSync()', function () { it('should read and parse JSON', function () { var file = path.join(TEST_DIR, 'somefile3.json') - var obj = {name: 'JP'} + var obj = { name: 'JP' } fs.writeFileSync(file, JSON.stringify(obj)) try { @@ -60,7 +60,7 @@ describe('+ readFileSync()', function () { jf.readFileSync(file) }) - var obj = jf.readFileSync(file, {throws: false}) + var obj = jf.readFileSync(file, { throws: false }) assert.strictEqual(obj, null) }) }) @@ -72,7 +72,7 @@ describe('+ readFileSync()', function () { fs.writeFileSync(file, data) assert.throws(function () { - jf.readFileSync(file, {throws: true}) + jf.readFileSync(file, { throws: true }) }) }) }) @@ -81,7 +81,7 @@ describe('+ readFileSync()', function () { it('should return null', function () { var file = path.join(TEST_DIR, 'somefile4-invalid.json') - var obj = jf.readFileSync(file, {throws: false}) + var obj = jf.readFileSync(file, { throws: false }) assert.strictEqual(obj, null) }) }) @@ -91,7 +91,7 @@ describe('+ readFileSync()', function () { var file = path.join(TEST_DIR, 'somefile4-invalid.json') assert.throws(function () { - jf.readFileSync(file, {throws: true}) + jf.readFileSync(file, { throws: true }) }) }) }) @@ -111,7 +111,7 @@ describe('+ readFileSync()', function () { } fs.writeFileSync(file, JSON.stringify(obj)) - var data = jf.readFileSync(file, {reviver: sillyReviver}) + var data = jf.readFileSync(file, { reviver: sillyReviver }) assert.strictEqual(data.name, 'jp') assert(data.day instanceof Date) assert.strictEqual(data.day.toISOString(), '2015-06-19T11:41:26.815Z') diff --git a/test/read-file.test.js b/test/read-file.test.js index ac98c2a..2fe1dc8 100644 --- a/test/read-file.test.js +++ b/test/read-file.test.js @@ -23,7 +23,7 @@ describe('+ readFile()', function () { it('should read and parse JSON', function (done) { var file = path.join(TEST_DIR, 'somefile.json') - var obj = {name: 'JP'} + var obj = { name: 'JP' } fs.writeFileSync(file, JSON.stringify(obj)) jf.readFile(file, function (err, obj2) { @@ -33,27 +33,63 @@ describe('+ readFile()', function () { }) }) + it('should resolve a promise with parsed JSON', function (done) { + var file = path.join(TEST_DIR, 'somefile.json') + var obj = { name: 'JP' } + fs.writeFileSync(file, JSON.stringify(obj)) + + jf.readFile(file) + .then((data) => { + assert.equal(data.name, obj.name) + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) + describe('> when invalid JSON', function () { - it('should include the filename in the error', function (done) { - var fn = 'somefile.json' - var file = path.join(TEST_DIR, fn) + var fn, file + + beforeEach(function (done) { + fn = 'somefile.json' + file = path.join(TEST_DIR, fn) fs.writeFileSync(file, '{') + done() + }) + it('should include the filename in the error', function (done) { jf.readFile(file, function (err, obj2) { assert(err instanceof Error) assert(err.message.match(fn)) done() }) }) + + it('should reject the promise with filename in error', function (done) { + jf.readFile(file) + .catch(err => { + assert(err instanceof Error) + assert(err.message.match(fn)) + done() + }) + }) }) describe('> when invalid JSON and throws set to false', function () { - it('should return null and no error', function (done) { - var fn = 'somefile4-invalid.json' - var file = path.join(TEST_DIR, fn) + var fn, file + + beforeEach(function (done) { + fn = 'somefile4-invalid.json' + file = path.join(TEST_DIR, fn) var data = '{not valid JSON' - var bothDone = false fs.writeFileSync(file, data) + done() + }) + + it('should return null and no error', function (done) { + var bothDone = false jf.readFile(file, function (err, obj2) { assert(err instanceof Error) @@ -64,7 +100,7 @@ describe('+ readFile()', function () { bothDone = true }) - jf.readFile(file, {throws: false}, function (err, obj2) { + jf.readFile(file, { throws: false }, function (err, obj2) { assert.ifError(err) assert.strictEqual(obj2, null) if (bothDone) { @@ -73,16 +109,33 @@ describe('+ readFile()', function () { bothDone = true }) }) + + it('should resolve the promise with null as data', function (done) { + jf.readFile(file, { throws: false }) + .then(data => { + assert.strictEqual(data, null) + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) }) describe('> when invalid JSON and throws set to true', function () { - it('should return an error', function (done) { - var fn = 'somefile4-invalid.json' - var file = path.join(TEST_DIR, fn) + var fn, file + + beforeEach(function (done) { + fn = 'somefile4-invalid.json' + file = path.join(TEST_DIR, fn) var data = '{not valid JSON' - var bothDone = false fs.writeFileSync(file, data) + done() + }) + it('should return an error', function (done) { + var bothDone = false jf.readFile(file, function (err, obj2) { assert(err instanceof Error) assert(err.message.match(fn)) @@ -92,7 +145,7 @@ describe('+ readFile()', function () { bothDone = true }) - jf.readFile(file, {throws: true}, function (err, obj2) { + jf.readFile(file, { throws: true }, function (err, obj2) { assert(err instanceof Error) assert(err.message.match(fn)) if (bothDone) { @@ -101,24 +154,36 @@ describe('+ readFile()', function () { bothDone = true }) }) + + it('should reject the promise', function (done) { + jf.readFile(file, { throws: true }) + .catch(err => { + assert(err instanceof Error) + assert(err.message.match(fn)) + done() + }) + }) }) describe('> when JSON reviver is set', function () { - it('should transform the JSON', function (done) { - var file = path.join(TEST_DIR, 'somefile.json') - var sillyReviver = function (k, v) { + var file, sillyReviver + + beforeEach(function (done) { + file = path.join(TEST_DIR, 'somefile.json') + sillyReviver = function (k, v) { if (typeof v !== 'string') return v if (v.indexOf('date:') < 0) return v return new Date(v.split('date:')[1]) } - var obj = { - name: 'jp', - day: 'date:2015-06-19T11:41:26.815Z' - } + var obj = { name: 'jp', day: 'date:2015-06-19T11:41:26.815Z' } fs.writeFileSync(file, JSON.stringify(obj)) - jf.readFile(file, {reviver: sillyReviver}, function (err, data) { + done() + }) + + it('should transform the JSON', function (done) { + jf.readFile(file, { reviver: sillyReviver }, function (err, data) { assert.ifError(err) assert.strictEqual(data.name, 'jp') assert(data.day instanceof Date) @@ -126,9 +191,22 @@ describe('+ readFile()', function () { done() }) }) + + it('should resolve the promise with transformed JSON', function (done) { + jf.readFile(file, { reviver: sillyReviver }) + .then(data => { + assert.strictEqual(data.name, 'jp') + assert(data.day instanceof Date) + assert.strictEqual(data.day.toISOString(), '2015-06-19T11:41:26.815Z') + done() + }).catch(err => { + assert.ifError(err) + done() + }) + }) }) - describe('> when passing null and callback', function () { + describe('> when passing null as options and callback', function () { it('should not throw an error', function (done) { var file = path.join(TEST_DIR, 'somefile.json') @@ -145,8 +223,8 @@ describe('+ readFile()', function () { }) }) - describe('> when passing encoding string as option', function () { - it('should not throw an error', function (done) { + describe('> when passing null as options and expecting a promise', function () { + it('should resolve the promise', function (done) { var file = path.join(TEST_DIR, 'somefile.json') var obj = { @@ -154,24 +232,80 @@ describe('+ readFile()', function () { } fs.writeFileSync(file, JSON.stringify(obj)) + jf.readFile(file, null) + .then(data => { + assert.strictEqual(data.name, obj.name) + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) + }) + + describe('> when passing encoding string as option', function () { + var file, obj + + beforeEach(function (done) { + file = path.join(TEST_DIR, 'somefile.json') + + obj = { + name: 'jp' + } + fs.writeFileSync(file, JSON.stringify(obj)) + done() + }) + + it('should not throw an error', function (done) { jf.readFile(file, 'utf8', function (err) { assert.ifError(err) assert.strictEqual(obj.name, 'jp') done() }) }) + + it('should resolve the promise', function (done) { + jf.readFile(file, 'utf8') + .then(data => { + assert.strictEqual(data.name, obj.name) + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) }) describe('> w/ BOM', function () { - it('should properly parse', function (done) { - var file = path.join(TEST_DIR, 'file-bom.json') - var obj = { name: 'JP' } + var file, obj + + beforeEach(function (done) { + file = path.join(TEST_DIR, 'file-bom.json') + obj = { name: 'JP' } fs.writeFileSync(file, '\uFEFF' + JSON.stringify(obj)) + done() + }) + + it('should properly parse', function (done) { jf.readFile(file, function (err, data) { assert.ifError(err) assert.deepEqual(obj, data) done() }) }) + + it('should resolve the promise with parsed JSON', function (done) { + jf.readFile(file) + .then(data => { + assert.deepEqual(data, obj) + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) }) }) diff --git a/test/write-file-sync.test.js b/test/write-file-sync.test.js index e70acc5..1c63181 100644 --- a/test/write-file-sync.test.js +++ b/test/write-file-sync.test.js @@ -23,7 +23,7 @@ describe('+ writeFileSync()', function () { it('should serialize the JSON and write it to file', function () { var file = path.join(TEST_DIR, 'somefile4.json') - var obj = {name: 'JP'} + var obj = { name: 'JP' } jf.writeFileSync(file, obj) @@ -47,7 +47,7 @@ describe('+ writeFileSync()', function () { reg: new RegExp(/hello/g) } - jf.writeFileSync(file, obj, {replacer: sillyReplacer}) + jf.writeFileSync(file, obj, { replacer: sillyReplacer }) var data = JSON.parse(fs.readFileSync(file)) assert.strictEqual(data.name, 'jp') assert.strictEqual(typeof data.reg, 'string') @@ -59,7 +59,7 @@ describe('+ writeFileSync()', function () { it('should write file with spaces', function () { var file = path.join(TEST_DIR, 'somefile.json') var obj = { name: 'JP' } - jf.writeFileSync(file, obj, {spaces: 8}) + jf.writeFileSync(file, obj, { spaces: 8 }) var data = fs.readFileSync(file, 'utf8') assert.strictEqual(data, JSON.stringify(obj, null, 8) + '\n') }) @@ -67,7 +67,7 @@ describe('+ writeFileSync()', function () { it('should use EOL override', function () { var file = path.join(TEST_DIR, 'somefile.json') var obj = { name: 'JP' } - jf.writeFileSync(file, obj, {spaces: 2, EOL: '***'}) + jf.writeFileSync(file, obj, { spaces: 2, EOL: '***' }) var data = fs.readFileSync(file, 'utf8') assert.strictEqual(data, '{*** "name": "JP"***}***') }) diff --git a/test/write-file.test.js b/test/write-file.test.js index 0334c54..a7c030d 100644 --- a/test/write-file.test.js +++ b/test/write-file.test.js @@ -23,7 +23,7 @@ describe('+ writeFile()', function () { it('should serialize and write JSON', function (done) { var file = path.join(TEST_DIR, 'somefile2.json') - var obj = {name: 'JP'} + var obj = { name: 'JP' } jf.writeFile(file, obj, function (err) { assert.ifError(err) @@ -39,20 +39,48 @@ describe('+ writeFile()', function () { }) }) + it('should write JSON, resolve promise', function (done) { + var file = path.join(TEST_DIR, 'somefile2.json') + var obj = { name: 'JP' } + + jf.writeFile(file, obj) + .then(res => { + fs.readFile(file, 'utf8', function (err, data) { + assert.ifError(err) + var obj2 = JSON.parse(data) + assert.equal(obj2.name, obj.name) + + // verify EOL + assert.equal(data[data.length - 1], '\n') + done() + }) + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) + describe('> when JSON replacer is set', function () { - it('should replace JSON', function (done) { - var file = path.join(TEST_DIR, 'somefile.json') - var sillyReplacer = function (k, v) { + var file, sillyReplacer, obj + + beforeEach(function (done) { + file = path.join(TEST_DIR, 'somefile.json') + sillyReplacer = function (k, v) { if (!(v instanceof RegExp)) return v return 'regex:' + v.toString() } - var obj = { + obj = { name: 'jp', reg: new RegExp(/hello/g) } - jf.writeFile(file, obj, {replacer: sillyReplacer}, function (err) { + done() + }) + + it('should replace JSON', function (done) { + jf.writeFile(file, obj, { replacer: sillyReplacer }, function (err) { assert.ifError(err) var data = JSON.parse(fs.readFileSync(file)) @@ -62,9 +90,24 @@ describe('+ writeFile()', function () { done() }) }) + + it('should replace JSON, resolve promise', function (done) { + jf.writeFile(file, obj, { replacer: sillyReplacer }) + .then(res => { + var data = JSON.parse(fs.readFileSync(file)) + assert.strictEqual(data.name, 'jp') + assert.strictEqual(typeof data.reg, 'string') + assert.strictEqual(data.reg, 'regex:/hello/g') + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) }) - describe('> when passing null and callback', function () { + describe('> when passing null as options and callback', function () { it('should not throw an error', function (done) { var file = path.join(TEST_DIR, 'somefile.json') var obj = { name: 'jp' } @@ -75,11 +118,31 @@ describe('+ writeFile()', function () { }) }) - describe('> when spaces passed as an option', function () { - it('should write file with spaces', function (done) { + describe('> when passing null as options and No callback', function () { + it('should not throw an error', function (done) { var file = path.join(TEST_DIR, 'somefile.json') var obj = { name: 'jp' } - jf.writeFile(file, obj, {spaces: 8}, function (err) { + jf.writeFile(file, obj, null) + .then(res => { + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) + }) + + describe('> when spaces passed as an option', function () { + var file, obj + beforeEach(function (done) { + file = path.join(TEST_DIR, 'somefile.json') + obj = { name: 'jp' } + done() + }) + + it('should write file with spaces', function (done) { + jf.writeFile(file, obj, { spaces: 8 }, function (err) { assert.ifError(err) var data = fs.readFileSync(file, 'utf8') assert.strictEqual(data, JSON.stringify(obj, null, 8) + '\n') @@ -87,22 +150,59 @@ describe('+ writeFile()', function () { }) }) + it('should write file with spaces, resolve the promise', function (done) { + jf.writeFile(file, obj, { spaces: 8 }) + .then(res => { + var data = fs.readFileSync(file, 'utf8') + assert.strictEqual(data, JSON.stringify(obj, null, 8) + '\n') + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) + }) + + describe('> when spaces, EOL are passed as options', function () { + var file, obj + beforeEach(function (done) { + file = path.join(TEST_DIR, 'somefile.json') + obj = { name: 'jp' } + done() + }) + it('should use EOL override', function (done) { - var file = path.join(TEST_DIR, 'somefile.json') - var obj = { name: 'jp' } - jf.writeFile(file, obj, {spaces: 2, EOL: '***'}, function (err) { + jf.writeFile(file, obj, { spaces: 2, EOL: '***' }, function (err) { assert.ifError(err) var data = fs.readFileSync(file, 'utf8') assert.strictEqual(data, '{*** "name": "jp"***}***') done() }) }) - }) + it('should use EOL override, resolve the promise', function (done) { + jf.writeFile(file, obj, { spaces: 2, EOL: '***' }) + .then(res => { + var data = fs.readFileSync(file, 'utf8') + assert.strictEqual(data, '{*** "name": "jp"***}***') + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) + }) describe('> when passing encoding string as options', function () { + var file, obj + beforeEach(function (done) { + file = path.join(TEST_DIR, 'somefile.json') + obj = { name: 'jp' } + done() + }) + it('should not error', function (done) { - var file = path.join(TEST_DIR, 'somefile.json') - var obj = { name: 'jp' } jf.writeFile(file, obj, 'utf8', function (err) { assert.ifError(err) var data = fs.readFileSync(file, 'utf8') @@ -110,11 +210,24 @@ describe('+ writeFile()', function () { done() }) }) + + it('should not error, resolve the promise', function (done) { + jf.writeFile(file, obj, 'utf8') + .then(res => { + var data = fs.readFileSync(file, 'utf8') + assert.strictEqual(data, JSON.stringify(obj) + '\n') + done() + }) + .catch(err => { + assert.ifError(err) + done() + }) + }) }) // Prevent https://github.com/jprichardson/node-jsonfile/issues/81 from happening describe("> when callback isn't passed & can't serialize", function () { - it('should not write an empty file', function (done) { + it('should not write an empty file, should reject the promise', function (done) { this.slow(1100) var file = path.join(TEST_DIR, 'somefile.json') var obj1 = { name: 'JP' } @@ -122,6 +235,9 @@ describe('+ writeFile()', function () { obj1.circular = obj2 jf.writeFile(file, obj1) + .catch(err => { + assert.ifError(err) + }) setTimeout(function () { assert(!fs.existsSync(file))