Files
atom/src/config-file.js
2018-03-27 08:54:37 -04:00

119 lines
2.9 KiB
JavaScript

const _ = require('underscore-plus')
const fs = require('fs-plus')
const dedent = require('dedent')
const {Emitter} = require('event-kit')
const {watchPath} = require('./path-watcher')
const CSON = require('season')
const Path = require('path')
const async = require('async')
const EVENT_TYPES = new Set([
'created',
'modified',
'renamed'
])
module.exports =
class ConfigFile {
static at (path) {
if (!this._known) {
this._known = new Map()
}
const existing = this._known.get(path)
if (existing) {
return existing
}
const created = new ConfigFile(path)
this._known.set(path, created)
return created
}
constructor (path) {
this.path = path
this.emitter = new Emitter()
this.value = {}
this.reloadCallbacks = []
// Use a queue to prevent multiple concurrent write to the same file.
const writeQueue = async.queue((data, callback) =>
CSON.writeFile(this.path, data, error => {
if (error) {
this.emitter.emit('did-error', dedent `
Failed to write \`${Path.basename(this.path)}\`.
${error.message}
`)
}
callback()
})
)
this.requestLoad = _.debounce(() => this.reload(), 200)
this.requestSave = _.debounce((data) => writeQueue.push(data), 200)
}
get () {
return this.value
}
update (value) {
return new Promise(resolve => {
this.requestSave(value)
this.reloadCallbacks.push(resolve)
})
}
async watch (callback) {
if (!fs.existsSync(this.path)) {
fs.makeTreeSync(Path.dirname(this.path))
CSON.writeFileSync(this.path, {}, {flag: 'wx'})
}
await this.reload()
try {
const watcher = await watchPath(this.path, {}, events => {
if (events.some(event => EVENT_TYPES.has(event.action))) this.requestLoad()
})
return watcher
} catch (error) {
this.emitter.emit('did-error', dedent `
Unable to watch path: \`${Path.basename(this.path)}\`.
Make sure you have permissions to \`${this.path}\`.
On linux there are currently problems with watch sizes.
See [this document][watches] for more info.
[watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path\
`)
}
}
onDidChange (callback) {
return this.emitter.on('did-change', callback)
}
onDidError (callback) {
return this.emitter.on('did-error', callback)
}
reload () {
return new Promise(resolve => {
CSON.readFile(this.path, (error, data) => {
if (error) {
this.emitter.emit('did-error', `Failed to load \`${Path.basename(this.path)}\` - ${error.message}`)
} else {
this.value = data || {}
this.emitter.emit('did-change', this.value)
for (const callback of this.reloadCallbacks) callback()
this.reloadCallbacks.length = 0
}
resolve()
})
})
}
}