-
-
Notifications
You must be signed in to change notification settings - Fork 131
/
config-file.js
129 lines (110 loc) · 3.46 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
124
125
126
127
128
129
const _ = require('underscore-plus');
const fs = require('fs-plus');
const dedent = require('dedent');
const { Disposable, Emitter } = require('event-kit');
const CSON = require('season');
const Path = require('path');
const asyncQueue = require('async/queue');
// TODO: if we ever decide to change path watchers on the future, this is kinda
// duplicated because of https://github.com/pulsar-edit/pulsar/issues/76
const nsfw = require('nsfw');
const EVENT_TYPES = new Set([nsfw.actions.CREATED, nsfw.actions.MODIFIED, nsfw.actions.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() {
if (!fs.existsSync(this.path)) {
fs.makeTreeSync(Path.dirname(this.path));
CSON.writeFileSync(this.path, {}, { flag: 'wx' });
}
await this.reload();
try {
const watcher = await nsfw(this.path, events => {
if (events.some(event => EVENT_TYPES.has(event.action))) {
this.requestLoad();
}
})
watcher.start();
return { dispose: () => watcher.stop() };
} catch (error) {
//TODO_PULSAR: Find out why the atom global variable isn't available at this point
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://pulsar-edit.dev/docs/atom-archive/hacking-atom/#typeerror-unable-to-watch-path
`//TODO: Update the above to the pulsar docs if we choose to add this
);
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();
});
});
}
};