1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 |
|
7 | var _walkthroughtree = require('./walkthroughtree');
|
8 |
|
9 | Object.keys(_walkthroughtree).forEach(function (key) {
|
10 | if (key === "default" || key === "__esModule") return;
|
11 | Object.defineProperty(exports, key, {
|
12 | enumerable: true,
|
13 | get: function () {
|
14 | return _walkthroughtree[key];
|
15 | }
|
16 | });
|
17 | });
|
18 |
|
19 | var _fs = require('fs');
|
20 |
|
21 | var _fs2 = _interopRequireDefault(_fs);
|
22 |
|
23 | var _path = require('path');
|
24 |
|
25 | var _path2 = _interopRequireDefault(_path);
|
26 |
|
27 | var _events = require('events');
|
28 |
|
29 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
30 |
|
31 | const getOwnPropertyHashes = typeof Reflect === 'object' && Reflect.ownKeys || (Object.getOwnPropertySymbols ? function getOwnPropertyHashes(obj) {
|
32 | return Array.prototype.concat(Object.getOwnPropertyNames(obj), Object.getOwnPropertySymbols(obj));
|
33 | } : Object.getOwnPropertyNames);
|
34 |
|
35 | const privateProps = typeof Symbol === 'function' ? Symbol('internals') : '__internals';
|
36 |
|
37 | function isObject(o) {
|
38 | if (!o) return false;
|
39 | var type = typeof o;
|
40 | return type === 'object' || type === 'function';
|
41 | }
|
42 |
|
43 | function getAllProperties(obj, output = []) {
|
44 | if (!obj) return output;
|
45 | Array.prototype.push.apply(output, getOwnPropertyHashes(obj));
|
46 | return getAllProperties(Object.getPrototypeOf(obj), output);
|
47 | }
|
48 |
|
49 | function fileChanged(a, b, deferred) {
|
50 | const internals = this[privateProps],
|
51 | { options } = internals;
|
52 | if (options.delay) {
|
53 | if (internals.fileChangeTimeout) {
|
54 | clearTimeout(internals.fileChangeTimeout);
|
55 | delete internals.fileChangeTimeout;
|
56 | }
|
57 | if (!deferred && !internals.updateFileLock) {
|
58 | internals.fileChangeTimeout = setTimeout(internals.fileChanged, options.delay, a, b, true);
|
59 | return;
|
60 | }
|
61 | }
|
62 | if (internals.updateFileLock) return;
|
63 | try {
|
64 | if (a instanceof _fs2.default.Stats ? a.mtime === b.mtime : b !== internals.fileName) return;
|
65 | this.forceUpdate();
|
66 | } catch (err) {
|
67 | console.log(err.stack || err);
|
68 | }
|
69 | }
|
70 |
|
71 | function deepPatchLayer(srcObject, srcDescriptor, patchDescriptor, key, depth, options) {
|
72 |
|
73 | if (!depth || !srcDescriptor) return Object.defineProperty(srcObject, key, patchDescriptor);
|
74 |
|
75 | const srcValue = srcDescriptor.value;
|
76 | const patchValue = patchDescriptor.value;
|
77 | if (srcValue === patchValue || !isObject(srcValue) || !isObject(patchValue)) return Object.defineProperty(srcObject, key, patchDescriptor);
|
78 |
|
79 | const { ignoreProperties, keepNonExists } = options;
|
80 |
|
81 |
|
82 | const patchKeys = getOwnPropertyHashes(patchValue);
|
83 | for (const key of patchKeys) {
|
84 | if (ignoreProperties && ignoreProperties.includes(key)) continue;
|
85 |
|
86 | const srcDescriptor = Object.getOwnPropertyDescriptor(srcValue, key);
|
87 | if (srcDescriptor && !srcDescriptor.configurable) continue;
|
88 |
|
89 | const patchDescriptor = Object.getOwnPropertyDescriptor(patchValue, key);
|
90 | if (!srcDescriptor || !srcDescriptor.get && !patchDescriptor.get) this.next(srcValue, srcDescriptor, patchDescriptor, key, depth - 1, options);
|
91 | }
|
92 |
|
93 |
|
94 | if (!keepNonExists) for (const key of getOwnPropertyHashes(srcValue)) {
|
95 | if (patchKeys.includes(key) || ignoreProperties && ignoreProperties.includes(key)) continue;
|
96 |
|
97 | const srcDescriptor = Object.getOwnPropertyDescriptor(srcValue, key);
|
98 | if (!srcDescriptor.configurable) continue;
|
99 |
|
100 | delete srcValue[key];
|
101 | }
|
102 |
|
103 | return srcObject;
|
104 | }
|
105 |
|
106 | class SelfReloadJSON extends _events.EventEmitter {
|
107 | constructor(options) {
|
108 | super();
|
109 |
|
110 | switch (typeof options) {
|
111 | case 'string':
|
112 | options = { fileName: options };break;
|
113 | case 'object':case 'undefined':
|
114 | break;
|
115 | default:
|
116 | throw new Error('Invalid options type.');
|
117 | }
|
118 |
|
119 |
|
120 | this[privateProps] = null;
|
121 |
|
122 | this[privateProps] = {
|
123 | keys: [],
|
124 | fileName: '',
|
125 | watcher: null,
|
126 | content: null,
|
127 | fileChanged: (a, b, deferred) => fileChanged.call(this, a, b, deferred),
|
128 | forceUpdate: () => this.forceUpdate(),
|
129 | omitKeys: getAllProperties(this),
|
130 | options: Object.assign({
|
131 | fileName: '',
|
132 | encoding: 'utf8',
|
133 | additive: false,
|
134 | method: 'native',
|
135 | interval: 5000,
|
136 | reviver: null,
|
137 | replacer: null,
|
138 | delay: 0,
|
139 | depth: -1
|
140 | }, options)
|
141 | };
|
142 |
|
143 |
|
144 |
|
145 | for (let key in this) {
|
146 | const value = this[key];
|
147 | delete this[key];
|
148 | Object.defineProperty(this, key, {
|
149 | value,
|
150 | enumerable: false,
|
151 | configurable: true,
|
152 | writable: true
|
153 | });
|
154 | }
|
155 |
|
156 | this.resume();
|
157 | }
|
158 |
|
159 | stop() {
|
160 | const internals = this[privateProps];
|
161 | if (!internals.watcher) return;
|
162 | if (typeof internals.watcher === 'string') _fs2.default.unwatchFile(internals.watcher, internals.fileChanged);else internals.watcher.close();
|
163 | internals.watcher = null;
|
164 | }
|
165 |
|
166 | resume() {
|
167 | this.stop();
|
168 | const internals = this[privateProps],
|
169 | { options } = internals;
|
170 |
|
171 | options.fileName = _path2.default.resolve(options.fileName);
|
172 | internals.fileName = _path2.default.basename(options.fileName);
|
173 |
|
174 | switch (options.method) {
|
175 | case 'native':
|
176 | internals.watcher = _fs2.default.watch(options.fileName, { encoding: options.encoding }, internals.fileChanged);
|
177 | break;
|
178 |
|
179 | case 'polling':
|
180 | internals.watcher = options.fileName;
|
181 | _fs2.default.watchFile(options.fileName, { interval: options.interval }, internals.fileChanged);
|
182 | break;
|
183 | }
|
184 | this.forceUpdate();
|
185 | }
|
186 |
|
187 | save(options) {
|
188 | const internals = this[privateProps];
|
189 | options = Object.assign({ space: null }, internals.options, options);
|
190 | internals.updateFileLock = true;
|
191 | try {
|
192 | const json = JSON.stringify(this, options.replacer, options.space);
|
193 | _fs2.default.writeFileSync(internals.options.fileName, json, options);
|
194 | internals.raw = json;
|
195 | } finally {
|
196 | internals.updateFileLock = false;
|
197 | }
|
198 | }
|
199 |
|
200 | forceUpdate() {
|
201 | const internals = this[privateProps],
|
202 | { options } = internals;
|
203 | if (internals.updateFileLock) return;
|
204 | internals.updateFileLock = true;
|
205 |
|
206 | if (internals.retryTimer) {
|
207 | clearTimeout(internals.retryTimer);
|
208 | delete internals.retryTimer;
|
209 | }
|
210 |
|
211 | if (internals.fileChangeTimeout) {
|
212 | clearTimeout(internals.fileChangeTimeout);
|
213 | delete internals.fileChangeTimeout;
|
214 | }
|
215 |
|
216 | try {
|
217 | const rawContent = _fs2.default.readFileSync(options.fileName, { encoding: options.encoding });
|
218 | if (internals.raw === rawContent) return;
|
219 | internals.raw = rawContent;
|
220 |
|
221 | let newContent = JSON.parse(rawContent, options.reviver);
|
222 | if (typeof newContent !== 'object') newContent = { value: newContent };
|
223 |
|
224 | SelfReloadJSON.deepPatch(this, newContent, {
|
225 | keepNonExists: options.additive,
|
226 | ignoreProperties: internals.omitKeys,
|
227 | depth: options.depth
|
228 | });
|
229 |
|
230 | internals.newContent = SelfReloadJSON.deepPatch(internals.newContent || {}, newContent, {
|
231 | keepNonExists: options.additive,
|
232 | depth: options.depth
|
233 | });
|
234 | } catch (err) {
|
235 | switch (err && err.code) {
|
236 | case 'EBUSY':
|
237 | case 'EAGAIN':
|
238 | internals.retryTimer = setTimeout(internals.forceUpdate, options.delay);
|
239 | return;
|
240 | }
|
241 |
|
242 | this.emit('error', err);
|
243 | return;
|
244 | } finally {
|
245 | internals.updateFileLock = false;
|
246 | }
|
247 |
|
248 | this.emit('updated', internals.newContent);
|
249 | }
|
250 |
|
251 | static deepPatch(source, patch, options = {}) {
|
252 | if (!isObject(source) || !isObject(patch)) return patch;
|
253 | const dummy = { value: source };
|
254 | return (0, _walkthroughtree.walkThroughTree)(deepPatchLayer, {
|
255 | firstResult: true
|
256 | }, dummy, dummy, {
|
257 | value: patch,
|
258 | configurable: true,
|
259 | enumerable: true,
|
260 | writable: true
|
261 | }, 'value', 'depth' in options ? options.depth : -1, options).value;
|
262 | }
|
263 | }
|
264 |
|
265 | exports.default = SelfReloadJSON;
|
266 | module.exports = exports['default']; |
\ | No newline at end of file |