mirror of
https://github.com/atom/atom.git
synced 2026-01-25 06:48:28 -05:00
119 lines
2.9 KiB
JavaScript
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()
|
|
})
|
|
})
|
|
}
|
|
}
|