Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add appendFile/appendFileSync support #65

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ node_js:
- "4"
- "5"
- "6"
- "7"
matrix:
include:
- node_js: "4"
Expand Down
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ environment:
- nodejs_version: "4"
- nodejs_version: "5"
- nodejs_version: "6"
- nodejs_version: "7"

# Install scripts. (runs after repo cloning)
install:
Expand Down
96 changes: 95 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,106 @@ function stripBom (content) {
return content
}

function appendFile (file, obj, options, callback) {
if (callback == null) {
callback = options
options = {}
}
options = options || {}
var fs = options.fs || _fs

var spaces = typeof options === 'object' && options !== null
? 'spaces' in options
? options.spaces : this.spaces
: this.spaces

// check if the file already contains data
readFile(file, function (err, src) {
if (err) {
if (err.code === 'ENOENT') {
var str = JSON.stringify(obj, options.replacer, spaces) + '\n'
fs.writeFile(file, str, options, callback)
} else {
if (callback) return callback(err, null)
}
}

try {
var srckeys = Object.keys(src)
Object.keys(obj).forEach(function (k) {
if (srckeys.indexOf(k) === -1 && !src.hasOwnProperty(k)) {
src[k] = obj[k]
}
if (srckeys.indexOf(k) !== -1) {
if (Array.isArray(src[k]) && Array.isArray(obj[k])) {
src[k] = src[k].concat(obj[k])
}
if (!Array.isArray(src[k]) && typeof src[k] === 'object') {
src[k] = Object.assign(src[k], obj[k])
}
if (!Array.isArray(src[k]) && typeof src[k] !== 'object') {
src[k] = obj[k]
}
}
})
src = JSON.stringify(src, options ? options.replacer : null, spaces) + '\n'
} catch (err) {
if (callback) return callback(err, null)
}

fs.writeFile(file, src, options, callback)
})
}

function appendFileSync (file, obj, options) {
options = options || {}
var fs = options.fs || _fs

var spaces = typeof options === 'object' && options !== null
? 'spaces' in options
? options.spaces : this.spaces
: this.spaces

var src
try {
src = readFileSync(file)
var srckeys = Object.keys(src)
Object.keys(obj).forEach(function (k) {
if (srckeys.indexOf(k) === -1 && !src.hasOwnProperty(k)) {
src[k] = obj[k]
}
if (srckeys.indexOf(k) !== -1) {
if (Array.isArray(src[k]) && Array.isArray(obj[k])) {
src[k] = src[k].concat(obj[k])
}
if (!Array.isArray(src[k]) && typeof src[k] === 'object') {
src[k] = Object.assign(src[k], obj[k])
}
if (!Array.isArray(src[k]) && typeof src[k] !== 'object') {
src[k] = obj[k]
}
}
})
src = JSON.stringify(src, options.replacer, spaces) + '\n'
return fs.writeFileSync(file, src, options)
} catch (err) {
if (err.code === 'ENOENT') {
src = JSON.stringify(obj, options.replacer, spaces) + '\n'
return fs.writeFileSync(file, src, options)
} else {
throw err
}
}
}

var jsonfile = {
spaces: null,
readFile: readFile,
readFileSync: readFileSync,
writeFile: writeFile,
writeFileSync: writeFileSync
writeFileSync: writeFileSync,
appendFile: appendFile,
appendFileSync: appendFileSync
}

module.exports = jsonfile
99 changes: 99 additions & 0 deletions test/append-file-sync.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
var assert = require('assert')
var fs = require('fs')
var os = require('os')
var path = require('path')
var rimraf = require('rimraf')
var jf = require('../')

/* global describe it beforeEach afterEach */

describe('+ appendFileSync()', function () {
var TEST_DIR
var file
var obj
beforeEach(function (done) {
TEST_DIR = path.join(os.tmpdir(), 'jsonfile-tests-appendfile-sync')
rimraf.sync(TEST_DIR)
fs.mkdirSync(TEST_DIR)
file = path.join(TEST_DIR, 'someexistedfile.json')
obj = {name: 'jp', email: 'rand@some.com', msgs: [{id: 0, rec: 'somerec'}, {id: 1, rec: 'otherrec'}], colors: ['gray', 'blue', 'magenta']}
fs.writeFileSync(file, JSON.stringify(obj))
done()
})

afterEach(function (done) {
rimraf.sync(TEST_DIR)
done()
})

it('should serialize the JSON and append it to file', function () {
var dataToAppend = {email: 'updemail@some.com', msgs: [{id: 2, rec: 'someOtherRec'}], colors: ['red'], math: 'fun', animals: ['octopus', 'monkey']}
var afterAppend = '{"name":"jp","email":"updemail@some.com","msgs":[{"id":0,"rec":"somerec"},{"id":1,"rec":"otherrec"},{"id":2,"rec":"someOtherRec"}],"colors":["gray","blue","magenta","red"],"math":"fun","animals":["octopus","monkey"]}\n'

jf.appendFileSync(file, dataToAppend)

var data = fs.readFileSync(file, 'utf8')
var obj2 = JSON.parse(data)
assert.equal(data[data.length - 1], '\n')
assert.equal(obj2.name, obj.name)
assert.equal(obj2.email, dataToAppend.email)
assert.equal(obj2.colors.length, dataToAppend.colors.length + obj.colors.length)
assert.equal(obj2.animals.length, dataToAppend.animals.length)
assert.strictEqual(data, afterAppend)
})

describe('> when global spaces is set', function () {
it('should append JSON with spacing', function () {
var dataToAppend = {email: 'updemail@some.com', msgs: {id: 2, rec: 'someOtherRec'}, colors: ['red'], math: 'fun', animals: ['octopus', 'monkey']}
var afterAppend = '{\n "name": "jp",\n "email": "updemail@some.com",\n "msgs": [\n {\n "id": 0,\n "rec": "somerec"\n },\n {\n "id": 1,\n "rec": "otherrec"\n }\n ],\n "colors": [\n "gray",\n "blue",\n "magenta",\n "red"\n ],\n "math": "fun",\n "animals": [\n "octopus",\n "monkey"\n ]\n}\n'
jf.spaces = 2
jf.appendFileSync(file, dataToAppend)

var data = fs.readFileSync(file, 'utf8')
assert.strictEqual(data, afterAppend)
jf.spaces = null
})
})

describe('> when JSON replacer is set', function () {
it('should replace JSON', function () {
var sillyReplacer = function (k, v) {
if (!(v instanceof RegExp)) return v
return 'regex:' + v.toString()
}
var dataToAppend = {email: 'updemail@some.com', msgs: {id: 2, rec: 'someOtherRec'}, colors: ['red'], math: 'fun', animals: ['octopus', 'monkey'], reg: new RegExp(/hello/g)}

jf.appendFileSync(file, dataToAppend, {replacer: sillyReplacer})
var data = JSON.parse(fs.readFileSync(file))
assert.strictEqual(data.name, obj.name)
assert.strictEqual(data.email, dataToAppend.email)
assert.strictEqual(data.math, dataToAppend.math)
assert.strictEqual(data.colors.length, dataToAppend.colors.length + obj.colors.length)
assert.strictEqual(data.animals.length, dataToAppend.animals.length)
assert.strictEqual(typeof data.reg, 'string')
assert.strictEqual(data.reg, 'regex:/hello/g')
})
})

describe('> when spaces passed as an option', function () {
it('should append file with spaces', function () {
var dataToAppend = {email: 'updemail@some.com', msgs: {id: 2, rec: 'someOtherRec'}, colors: ['red'], math: 'fun', animals: ['octopus', 'monkey']}
var afterAppend = '{\n "name": "jp",\n "email": "updemail@some.com",\n "msgs": [\n {\n "id": 0,\n "rec": "somerec"\n },\n {\n "id": 1,\n "rec": "otherrec"\n }\n ],\n "colors": [\n "gray",\n "blue",\n "magenta",\n "red"\n ],\n "math": "fun",\n "animals": [\n "octopus",\n "monkey"\n ]\n}\n'
jf.appendFileSync(file, dataToAppend, {spaces: 8})
var data = fs.readFileSync(file, 'utf8')
assert.strictEqual(data, afterAppend)
jf.spaces = null
})
})

describe('> when passing encoding string as options', function () {
it('should not error', function () {
var dataToAppend = {email: 'updemail@some.com', msgs: {id: 2, rec: 'someOtherRec'}, colors: ['red'], math: 'fun', animals: ['octopus', 'monkey']}
var afterAppend = '{"name":"jp","email":"updemail@some.com","msgs":[{"id":0,"rec":"somerec"},{"id":1,"rec":"otherrec"}],"colors":["gray","blue","magenta","red"],"math":"fun","animals":["octopus","monkey"]}\n'

jf.appendFileSync(file, dataToAppend, 'utf8')
var data = fs.readFileSync(file, 'utf8')
assert.strictEqual(data, afterAppend)
})
})
})
130 changes: 130 additions & 0 deletions test/append-file.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
var assert = require('assert')
var fs = require('fs')
var os = require('os')
var path = require('path')
var rimraf = require('rimraf')
var jf = require('../')

/* global describe it beforeEach afterEach */

describe('+ appendFile()', function () {
var TEST_DIR
var file
var obj
beforeEach(function (done) {
TEST_DIR = path.join(os.tmpdir(), 'jsonfile-tests-appendfile')
rimraf.sync(TEST_DIR)
fs.mkdirSync(TEST_DIR)
file = path.join(TEST_DIR, 'someexistedfile.json')
obj = {name: 'jp', email: 'rand@some.com', msgs: [{id: 0, rec: 'somerec'}, {id: 1, rec: 'otherrec'}], colors: ['gray', 'blue', 'magenta']}
fs.writeFileSync(file, JSON.stringify(obj))
done()
})

afterEach(function (done) {
rimraf.sync(TEST_DIR)
done()
})

it('should serialize and append JSON', function (done) {
var dataToAppend = {email: 'updemail@some.com', msgs: [{id: 2, rec: 'someOtherRec'}], colors: ['red'], math: 'fun', animals: ['octopus', 'monkey']}
var afterAppend = '{"name":"jp","email":"updemail@some.com","msgs":[{"id":0,"rec":"somerec"},{"id":1,"rec":"otherrec"},{"id":2,"rec":"someOtherRec"}],"colors":["gray","blue","magenta","red"],"math":"fun","animals":["octopus","monkey"]}\n'

jf.appendFile(file, dataToAppend, function (err) {
assert.ifError(err)
fs.readFile(file, 'utf8', function (err, data) {
assert.ifError(err)
var obj2 = JSON.parse(data)
assert.equal(data[data.length - 1], '\n')
assert.equal(obj2.name, obj.name)
assert.equal(obj2.email, dataToAppend.email)
assert.equal(obj2.colors.length, dataToAppend.colors.length + obj.colors.length)
assert.equal(obj2.animals.length, dataToAppend.animals.length)
assert.strictEqual(data, afterAppend)
done()
})
})
})

describe('> when global spaces is set', function () {
it('should append JSON with spacing', function (done) {
var dataToAppend = {email: 'updemail@some.com', msgs: {id: 2, rec: 'someOtherRec'}, colors: ['red'], math: 'fun', animals: ['octopus', 'monkey']}
var afterAppend = '{\n "name": "jp",\n "email": "updemail@some.com",\n "msgs": [\n {\n "id": 0,\n "rec": "somerec"\n },\n {\n "id": 1,\n "rec": "otherrec"\n }\n ],\n "colors": [\n "gray",\n "blue",\n "magenta",\n "red"\n ],\n "math": "fun",\n "animals": [\n "octopus",\n "monkey"\n ]\n}\n'
jf.spaces = 2
jf.appendFile(file, dataToAppend, function (err) {
assert.ifError(err)
var data = fs.readFileSync(file, 'utf8')
assert.strictEqual(data, afterAppend)
jf.spaces = null
done()
})
})
})

describe('> when JSON replacer is set', function () {
it('should replace JSON', function (done) {
var sillyReplacer = function (k, v) {
if (!(v instanceof RegExp)) return v
return 'regex:' + v.toString()
}
var dataToAppend = {email: 'updemail@some.com', msgs: {id: 2, rec: 'someOtherRec'}, colors: ['red'], math: 'fun', animals: ['octopus', 'monkey'], reg: new RegExp(/hello/g)}

jf.appendFile(file, dataToAppend, {replacer: sillyReplacer}, function (err) {
assert.ifError(err)
var data = JSON.parse(fs.readFileSync(file))
assert.strictEqual(data.name, obj.name)
assert.strictEqual(data.email, dataToAppend.email)
assert.strictEqual(data.math, dataToAppend.math)
assert.strictEqual(data.colors.length, dataToAppend.colors.length + obj.colors.length)
assert.strictEqual(data.animals.length, dataToAppend.animals.length)
assert.strictEqual(typeof data.reg, 'string')
assert.strictEqual(data.reg, 'regex:/hello/g')
done()
})
})
})

describe('> when passing null and callback', function () {
it('should not throw an error', function (done) {
var dataToAppend = {email: 'updemail@some.com', msgs: [{id: 2, rec: 'someOtherRec'}], colors: ['red'], math: 'fun', animals: ['octopus', 'monkey']}
var afterAppend = '{"name":"jp","email":"updemail@some.com","msgs":[{"id":0,"rec":"somerec"},{"id":1,"rec":"otherrec"},{"id":2,"rec":"someOtherRec"}],"colors":["gray","blue","magenta","red"],"math":"fun","animals":["octopus","monkey"]}\n'

jf.appendFile(file, dataToAppend, null, function (err) {
assert.ifError(err)
fs.readFile(file, 'utf8', function (err, data) {
assert.ifError(err)
assert.strictEqual(data, afterAppend)
done()
})
})
})
})

describe('> when spaces passed as an option', function () {
it('should append file with spaces', function (done) {
var dataToAppend = {email: 'updemail@some.com', msgs: {id: 2, rec: 'someOtherRec'}, colors: ['red'], math: 'fun', animals: ['octopus', 'monkey']}
var afterAppend = '{\n "name": "jp",\n "email": "updemail@some.com",\n "msgs": [\n {\n "id": 0,\n "rec": "somerec"\n },\n {\n "id": 1,\n "rec": "otherrec"\n }\n ],\n "colors": [\n "gray",\n "blue",\n "magenta",\n "red"\n ],\n "math": "fun",\n "animals": [\n "octopus",\n "monkey"\n ]\n}\n'

jf.appendFile(file, dataToAppend, {spaces: 8}, function (err) {
assert.ifError(err)
var data = fs.readFileSync(file, 'utf8')
assert.strictEqual(data, afterAppend)
done()
})
})
})

describe('> when passing encoding string as options', function () {
it('should not error', function (done) {
var dataToAppend = {email: 'updemail@some.com', msgs: {id: 2, rec: 'someOtherRec'}, colors: ['red'], math: 'fun', animals: ['octopus', 'monkey']}
var afterAppend = '{"name":"jp","email":"updemail@some.com","msgs":[{"id":0,"rec":"somerec"},{"id":1,"rec":"otherrec"}],"colors":["gray","blue","magenta","red"],"math":"fun","animals":["octopus","monkey"]}\n'

jf.appendFile(file, dataToAppend, 'utf8', function (err) {
assert.ifError(err)
var data = fs.readFileSync(file, 'utf8')
assert.strictEqual(data, afterAppend)
done()
})
})
})
})