forked from atom/atom
-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathconfig-file.js
123 lines (104 loc) · 3.09 KB
/
config-file.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
const _ = require('underscore-plus');
const fs = require('fs-plus');
const dedent = require('dedent');
const { Disposable, Emitter } = require('event-kit');
const { watchPath } = require('./path-watcher');
const CSON = require('season');
const Path = require('path');
const asyncQueue = require('async/queue');
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 = asyncQueue((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 {
return await watchPath(this.path, {}, events => {
if (events.some(event => EVENT_TYPES.has(event.action)))
this.requestLoad();
});
} 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\
`
);
return new Disposable();
}
}
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();
});
});
}
};