@schalkneethling/miyagi-core 4.4.0 → 4.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/default-config.js +170 -169
- package/lib/init/router.js +205 -205
- package/lib/init/watcher.js +704 -671
- package/lib/validator/schemas.js +58 -0
- package/package.json +1 -1
package/lib/init/watcher.js
CHANGED
|
@@ -22,10 +22,10 @@ import setViews from "./views.js";
|
|
|
22
22
|
const SOCKET_PATH = "/__miyagi_ws";
|
|
23
23
|
const sockets = new Set();
|
|
24
24
|
const DEFAULT_RELOAD_MESSAGE = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
type: "reload",
|
|
26
|
+
scope: "iframe",
|
|
27
|
+
reason: "change",
|
|
28
|
+
paths: [],
|
|
29
29
|
};
|
|
30
30
|
const RELOAD_SCOPES = new Set(["none", "iframe", "parent"]);
|
|
31
31
|
let restartFileWatcher = null;
|
|
@@ -35,22 +35,22 @@ let restartFileWatcher = null;
|
|
|
35
35
|
* @returns {number}
|
|
36
36
|
*/
|
|
37
37
|
function getEventPriority(eventType) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
// Higher value = stronger event for the same path during burst coalescing.
|
|
39
|
+
// Example: if a file emits "change" then "unlink", we keep "unlink".
|
|
40
|
+
switch (eventType) {
|
|
41
|
+
case "unlinkDir":
|
|
42
|
+
return 5;
|
|
43
|
+
case "unlink":
|
|
44
|
+
return 4;
|
|
45
|
+
case "addDir":
|
|
46
|
+
return 3;
|
|
47
|
+
case "add":
|
|
48
|
+
return 2;
|
|
49
|
+
case "change":
|
|
50
|
+
return 1;
|
|
51
|
+
default:
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -58,8 +58,8 @@ function getEventPriority(eventType) {
|
|
|
58
58
|
* @returns {string}
|
|
59
59
|
*/
|
|
60
60
|
function normalizeRelativePath(inputPath) {
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
const absolutePath = path.resolve(inputPath);
|
|
62
|
+
return path.relative(process.cwd(), absolutePath);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
@@ -67,11 +67,11 @@ function normalizeRelativePath(inputPath) {
|
|
|
67
67
|
* @returns {string}
|
|
68
68
|
*/
|
|
69
69
|
function resolveSourcePath(source) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
if (path.isAbsolute(source.path)) {
|
|
71
|
+
return source.path;
|
|
72
|
+
}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
return path.resolve(process.cwd(), source.path);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/**
|
|
@@ -79,27 +79,28 @@ function resolveSourcePath(source) {
|
|
|
79
79
|
* @param {object} [payload]
|
|
80
80
|
*/
|
|
81
81
|
function sendReload(scope, payload = {}) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
82
|
+
const normalizedScope = RELOAD_SCOPES.has(scope) ? scope : "parent";
|
|
83
|
+
const reloadEnabled =
|
|
84
|
+
global.config.watch?.reload?.enabled ?? global.config.ui.reload;
|
|
85
|
+
|
|
86
|
+
if (!reloadEnabled || normalizedScope === "none") {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const message = JSON.stringify({
|
|
91
|
+
...DEFAULT_RELOAD_MESSAGE,
|
|
92
|
+
...payload,
|
|
93
|
+
scope: normalizedScope,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
for (const ws of sockets) {
|
|
97
|
+
if (ws.readyState !== 1) {
|
|
98
|
+
sockets.delete(ws);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
ws.send(message);
|
|
103
|
+
}
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
/**
|
|
@@ -109,19 +110,19 @@ function sendReload(scope, payload = {}) {
|
|
|
109
110
|
* @returns {string}
|
|
110
111
|
*/
|
|
111
112
|
function colorize(color, value, useColors) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
113
|
+
if (!useColors) {
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const colors = {
|
|
118
|
+
cyan: "\x1b[36m",
|
|
119
|
+
green: "\x1b[32m",
|
|
120
|
+
yellow: "\x1b[33m",
|
|
121
|
+
grey: "\x1b[90m",
|
|
122
|
+
reset: "\x1b[0m",
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return `${colors[color] || ""}${value}${colors.reset}`;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
/**
|
|
@@ -129,65 +130,67 @@ function colorize(color, value, useColors) {
|
|
|
129
130
|
* @param {object} watchConfig
|
|
130
131
|
*/
|
|
131
132
|
function printWatchReport(report, watchConfig) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
133
|
+
const reportConfig = watchConfig?.report || {};
|
|
134
|
+
if (!reportConfig.enabled || !reportConfig.onStart) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (reportConfig.format === "json") {
|
|
139
|
+
console.info(JSON.stringify(report));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const useColors = reportConfig.useColors && process.stdout.isTTY;
|
|
144
|
+
|
|
145
|
+
if (reportConfig.format === "summary") {
|
|
146
|
+
console.info(
|
|
147
|
+
`${colorize("cyan", "Watch report:", useColors)} backend=${
|
|
148
|
+
report.backend
|
|
149
|
+
} sources=${report.meta.sourceCount} ignores=${report.meta.ignoreCount}`,
|
|
150
|
+
);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.info(colorize("cyan", "\nWatch report", useColors));
|
|
155
|
+
console.info(
|
|
156
|
+
` ${colorize("grey", "Watcher backend:", useColors)} ${colorize(
|
|
157
|
+
"green",
|
|
158
|
+
report.backend,
|
|
159
|
+
useColors,
|
|
160
|
+
)}`,
|
|
161
|
+
);
|
|
162
|
+
console.info(
|
|
163
|
+
` ${colorize("grey", "Diagnostics:", useColors)} sources=${
|
|
164
|
+
report.meta.sourceCount
|
|
165
|
+
}, ignores=${report.meta.ignoreCount}`,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if (watchConfig?.debug?.logResolvedSources) {
|
|
169
|
+
console.info(` ${colorize("grey", "Resolved sources:", useColors)}`);
|
|
170
|
+
|
|
171
|
+
for (const source of report.sources) {
|
|
172
|
+
const stateLabel = source.exists ? "exists" : "missing";
|
|
173
|
+
const stateColor = source.exists ? "green" : "yellow";
|
|
174
|
+
console.info(
|
|
175
|
+
` - ${source.id} (${source.type}) ${source.resolvedPath} [${colorize(
|
|
176
|
+
stateColor,
|
|
177
|
+
stateLabel,
|
|
178
|
+
useColors,
|
|
179
|
+
)}]`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
console.info(` ${colorize("grey", "Ignored patterns:", useColors)}`);
|
|
185
|
+
for (const pattern of report.ignore.patterns) {
|
|
186
|
+
console.info(` - ${pattern}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.info(` ${colorize("grey", "Reload rules:", useColors)}`);
|
|
190
|
+
for (const [key, scope] of Object.entries(report.reload.rules)) {
|
|
191
|
+
console.info(` - ${key}: ${scope}`);
|
|
192
|
+
}
|
|
193
|
+
console.info("");
|
|
191
194
|
}
|
|
192
195
|
|
|
193
196
|
/**
|
|
@@ -195,32 +198,32 @@ function printWatchReport(report, watchConfig) {
|
|
|
195
198
|
* @returns {object[]}
|
|
196
199
|
*/
|
|
197
200
|
function getExtensionSources(watchConfig) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
201
|
+
const extensionSources = [];
|
|
202
|
+
|
|
203
|
+
for (const extension of global.config.extensions) {
|
|
204
|
+
const ext = Array.isArray(extension) ? extension[0] : extension;
|
|
205
|
+
const opts =
|
|
206
|
+
Array.isArray(extension) && extension[1] ? extension[1] : { locales: {} };
|
|
207
|
+
|
|
208
|
+
if (!ext.extendWatcher) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const extensionWatch = ext.extendWatcher(opts);
|
|
213
|
+
if (!extensionWatch?.folder || !extensionWatch?.lang) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
extensionSources.push({
|
|
218
|
+
id: `extension-${extensionWatch.lang}`,
|
|
219
|
+
type: "dir",
|
|
220
|
+
path: path.join(extensionWatch.folder, extensionWatch.lang),
|
|
221
|
+
recursive: true,
|
|
222
|
+
optional: true,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return [...(watchConfig.sources || []), ...extensionSources];
|
|
224
227
|
}
|
|
225
228
|
|
|
226
229
|
/**
|
|
@@ -228,54 +231,54 @@ function getExtensionSources(watchConfig) {
|
|
|
228
231
|
* @returns {object}
|
|
229
232
|
*/
|
|
230
233
|
function resolveWatchTargets(watchConfig) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
234
|
+
const sources = getExtensionSources(watchConfig);
|
|
235
|
+
const ignorePatterns = [
|
|
236
|
+
...(watchConfig.ignore?.defaults ? ["node_modules/**", ".git/**"] : []),
|
|
237
|
+
...(watchConfig.ignore?.patterns || []),
|
|
238
|
+
];
|
|
239
|
+
const reportSources = [];
|
|
240
|
+
const targets = [];
|
|
241
|
+
|
|
242
|
+
for (const source of sources) {
|
|
243
|
+
if (!source || typeof source.path !== "string") continue;
|
|
244
|
+
const resolvedPath = resolveSourcePath(source);
|
|
245
|
+
const exists = fs.existsSync(resolvedPath);
|
|
246
|
+
|
|
247
|
+
reportSources.push({
|
|
248
|
+
id: source.id,
|
|
249
|
+
type: source.type,
|
|
250
|
+
inputPath: source.path,
|
|
251
|
+
resolvedPath,
|
|
252
|
+
exists,
|
|
253
|
+
recursive: source.recursive !== false,
|
|
254
|
+
optional: source.optional === true,
|
|
255
|
+
ignored: anymatch(ignorePatterns, normalizeRelativePath(resolvedPath)),
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
if (exists) {
|
|
259
|
+
targets.push(resolvedPath);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
targets: [...new Set(targets)].sort(),
|
|
265
|
+
ignorePatterns,
|
|
266
|
+
report: {
|
|
267
|
+
backend: "chokidar",
|
|
268
|
+
sources: reportSources.sort((a, b) => a.id.localeCompare(b.id)),
|
|
269
|
+
ignore: {
|
|
270
|
+
defaultsEnabled: Boolean(watchConfig.ignore?.defaults),
|
|
271
|
+
patterns: [...new Set(ignorePatterns)].sort(),
|
|
272
|
+
},
|
|
273
|
+
reload: {
|
|
274
|
+
rules: watchConfig.reload.rules,
|
|
275
|
+
},
|
|
276
|
+
meta: {
|
|
277
|
+
sourceCount: reportSources.length,
|
|
278
|
+
ignoreCount: [...new Set(ignorePatterns)].length,
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
};
|
|
279
282
|
}
|
|
280
283
|
|
|
281
284
|
/**
|
|
@@ -283,39 +286,39 @@ function resolveWatchTargets(watchConfig) {
|
|
|
283
286
|
* @returns {Promise<object>}
|
|
284
287
|
*/
|
|
285
288
|
async function updateFileContents(events) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
289
|
+
const data = helpers.cloneDeep(global.state.fileContents);
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
await Promise.all(
|
|
293
|
+
events.map(async ({ changedPath, relativePath }) => {
|
|
294
|
+
const fullPath = path.resolve(changedPath);
|
|
295
|
+
|
|
296
|
+
if (
|
|
297
|
+
fs.existsSync(fullPath) &&
|
|
298
|
+
fs.lstatSync(fullPath).isFile() &&
|
|
299
|
+
(helpers.fileIsTemplateFile(relativePath) ||
|
|
300
|
+
helpers.fileIsDataFile(relativePath) ||
|
|
301
|
+
helpers.fileIsDocumentationFile(relativePath) ||
|
|
302
|
+
helpers.fileIsSchemaFile(relativePath))
|
|
303
|
+
) {
|
|
304
|
+
try {
|
|
305
|
+
const result = await readFile(fullPath);
|
|
306
|
+
data[fullPath] = result;
|
|
307
|
+
return Promise.resolve();
|
|
308
|
+
} catch (err) {
|
|
309
|
+
return Promise.reject(err.message);
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
delete data[fullPath];
|
|
313
|
+
return Promise.resolve();
|
|
314
|
+
}
|
|
315
|
+
}),
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
return data;
|
|
319
|
+
} catch (err) {
|
|
320
|
+
log("error", err);
|
|
321
|
+
}
|
|
319
322
|
}
|
|
320
323
|
|
|
321
324
|
/**
|
|
@@ -323,7 +326,7 @@ async function updateFileContents(events) {
|
|
|
323
326
|
* @returns {boolean}
|
|
324
327
|
*/
|
|
325
328
|
function pathExistsAsDirectory(changedPath) {
|
|
326
|
-
|
|
329
|
+
return fs.existsSync(changedPath) && fs.lstatSync(changedPath).isDirectory();
|
|
327
330
|
}
|
|
328
331
|
|
|
329
332
|
/**
|
|
@@ -331,248 +334,261 @@ function pathExistsAsDirectory(changedPath) {
|
|
|
331
334
|
* @returns {Promise<void>}
|
|
332
335
|
*/
|
|
333
336
|
async function handleFileChange(events) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
337
|
+
for (const extension of global.config.extensions) {
|
|
338
|
+
const ext = Array.isArray(extension) ? extension[0] : extension;
|
|
339
|
+
const opts =
|
|
340
|
+
Array.isArray(extension) && extension[1] ? extension[1] : { locales: {} };
|
|
341
|
+
|
|
342
|
+
if (ext.callbacks?.fileChanged) {
|
|
343
|
+
await ext.callbacks.fileChanged(opts);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const watchRules = global.config.watch.reload.rules;
|
|
348
|
+
const templateEvents = events.filter(({ relativePath }) =>
|
|
349
|
+
helpers.fileIsTemplateFile(relativePath),
|
|
350
|
+
);
|
|
351
|
+
const dataEvents = events.filter(({ relativePath }) =>
|
|
352
|
+
helpers.fileIsDataFile(relativePath),
|
|
353
|
+
);
|
|
354
|
+
const docEvents = events.filter(({ relativePath }) =>
|
|
355
|
+
helpers.fileIsDocumentationFile(relativePath),
|
|
356
|
+
);
|
|
357
|
+
const schemaEvents = events.filter(({ relativePath }) =>
|
|
358
|
+
helpers.fileIsSchemaFile(relativePath),
|
|
359
|
+
);
|
|
360
|
+
const componentAssetEvents = events.filter(({ relativePath }) =>
|
|
361
|
+
helpers.fileIsAssetFile(relativePath),
|
|
362
|
+
);
|
|
363
|
+
const cssEvents = events.filter(({ relativePath }) =>
|
|
364
|
+
relativePath.endsWith(".css"),
|
|
365
|
+
);
|
|
366
|
+
const jsEvents = events.filter(({ relativePath }) =>
|
|
367
|
+
relativePath.endsWith(".js"),
|
|
368
|
+
);
|
|
369
|
+
const hasRemoveEvents = events.some(({ eventType }) =>
|
|
370
|
+
["unlink", "unlinkDir"].includes(eventType),
|
|
371
|
+
);
|
|
372
|
+
const hasDirectoryEvents = events.some(
|
|
373
|
+
({ changedPath, eventType }) =>
|
|
374
|
+
["addDir", "unlinkDir"].includes(eventType) ||
|
|
375
|
+
pathExistsAsDirectory(changedPath),
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
const configFilePath = global.config.userFileName
|
|
379
|
+
? path.resolve(process.cwd(), global.config.userFileName)
|
|
380
|
+
: null;
|
|
381
|
+
const hasConfigFileEvent =
|
|
382
|
+
configFilePath &&
|
|
383
|
+
events.some(
|
|
384
|
+
({ changedPath }) => path.resolve(changedPath) === configFilePath,
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
if (hasConfigFileEvent && global.config.watch.configFile.enabled) {
|
|
388
|
+
await configurationFileUpdated();
|
|
389
|
+
sendReload("parent", {
|
|
390
|
+
reason: "config",
|
|
391
|
+
paths: [global.config.userFileName],
|
|
392
|
+
});
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (
|
|
397
|
+
hasDirectoryEvents &&
|
|
398
|
+
!hasRemoveEvents &&
|
|
399
|
+
templateEvents.length === 0 &&
|
|
400
|
+
dataEvents.length === 0 &&
|
|
401
|
+
docEvents.length === 0 &&
|
|
402
|
+
schemaEvents.length === 0 &&
|
|
403
|
+
componentAssetEvents.length === 0 &&
|
|
404
|
+
cssEvents.length === 0 &&
|
|
405
|
+
jsEvents.length === 0
|
|
406
|
+
) {
|
|
407
|
+
await setState({
|
|
408
|
+
sourceTree: true,
|
|
409
|
+
fileContents: true,
|
|
410
|
+
menu: true,
|
|
411
|
+
partials: true,
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
sendReload(watchRules.unknown, {
|
|
415
|
+
reason: "directory",
|
|
416
|
+
paths: events.map(({ relativePath }) => relativePath),
|
|
417
|
+
});
|
|
418
|
+
log("success", `${t("updatingDone")}\n`);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (hasRemoveEvents) {
|
|
423
|
+
await setState({
|
|
424
|
+
sourceTree: true,
|
|
425
|
+
fileContents: await updateFileContents(events),
|
|
426
|
+
menu: true,
|
|
427
|
+
partials: true,
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
sendReload("parent", {
|
|
431
|
+
reason: "remove",
|
|
432
|
+
paths: events.map(({ relativePath }) => relativePath),
|
|
433
|
+
});
|
|
434
|
+
log("success", `${t("updatingDone")}\n`);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (templateEvents.length > 0) {
|
|
439
|
+
const allTemplatePathsExistAsPartials = templateEvents.every(
|
|
440
|
+
({ relativePath }) => {
|
|
441
|
+
const shortPath = relativePath.replace(
|
|
442
|
+
path.join(global.config.components.folder, "/"),
|
|
443
|
+
"",
|
|
444
|
+
);
|
|
445
|
+
return Object.keys(global.state.partials).includes(shortPath);
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
if (allTemplatePathsExistAsPartials) {
|
|
450
|
+
await setState({
|
|
451
|
+
fileContents: await updateFileContents(templateEvents),
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
sendReload(watchRules.template, {
|
|
455
|
+
reason: "template",
|
|
456
|
+
paths: templateEvents.map(({ relativePath }) => relativePath),
|
|
457
|
+
});
|
|
458
|
+
} else {
|
|
459
|
+
await setState({
|
|
460
|
+
fileContents: await updateFileContents(templateEvents),
|
|
461
|
+
sourceTree: true,
|
|
462
|
+
menu: true,
|
|
463
|
+
partials: true,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
sendReload("parent", {
|
|
467
|
+
reason: "template-added",
|
|
468
|
+
paths: templateEvents.map(({ relativePath }) => relativePath),
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
log("success", `${t("updatingDone")}\n`);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (dataEvents.length > 0) {
|
|
477
|
+
const hasBeenAdded = dataEvents.some(
|
|
478
|
+
({ eventType, changedPath }) =>
|
|
479
|
+
eventType === "add" ||
|
|
480
|
+
!Object.keys(global.state.fileContents).includes(
|
|
481
|
+
path.resolve(changedPath),
|
|
482
|
+
),
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
await setState({
|
|
486
|
+
fileContents: await updateFileContents(dataEvents),
|
|
487
|
+
sourceTree: hasBeenAdded,
|
|
488
|
+
menu: true,
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
sendReload(watchRules.data, {
|
|
492
|
+
reason: hasBeenAdded ? "data-added" : "data",
|
|
493
|
+
paths: dataEvents.map(({ relativePath }) => relativePath),
|
|
494
|
+
});
|
|
495
|
+
log("success", `${t("updatingDone")}\n`);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (docEvents.length > 0) {
|
|
500
|
+
const hasBeenAdded = docEvents.some(
|
|
501
|
+
({ eventType, changedPath }) =>
|
|
502
|
+
eventType === "add" ||
|
|
503
|
+
!Object.keys(global.state.fileContents).includes(
|
|
504
|
+
path.resolve(changedPath),
|
|
505
|
+
),
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
await setState({
|
|
509
|
+
fileContents: await updateFileContents(docEvents),
|
|
510
|
+
sourceTree: hasBeenAdded,
|
|
511
|
+
menu: hasBeenAdded,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
sendReload(watchRules.docs, {
|
|
515
|
+
reason: hasBeenAdded ? "docs-added" : "docs",
|
|
516
|
+
paths: docEvents.map(({ relativePath }) => relativePath),
|
|
517
|
+
});
|
|
518
|
+
log("success", `${t("updatingDone")}\n`);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (schemaEvents.length > 0) {
|
|
523
|
+
await setState({
|
|
524
|
+
fileContents: await updateFileContents(schemaEvents),
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
sendReload(watchRules.schema, {
|
|
528
|
+
reason: "schema",
|
|
529
|
+
paths: schemaEvents.map(({ relativePath }) => relativePath),
|
|
530
|
+
});
|
|
531
|
+
log("success", `${t("updatingDone")}\n`);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (componentAssetEvents.length > 0) {
|
|
536
|
+
sendReload(watchRules.componentAsset, {
|
|
537
|
+
reason: "component-asset",
|
|
538
|
+
paths: componentAssetEvents.map(({ relativePath }) => relativePath),
|
|
539
|
+
});
|
|
540
|
+
log("success", `${t("updatingDone")}\n`);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (cssEvents.length > 0) {
|
|
545
|
+
if (
|
|
546
|
+
cssEvents.some(({ relativePath }) =>
|
|
547
|
+
global.config.assets.customProperties.files.includes(relativePath),
|
|
548
|
+
)
|
|
549
|
+
) {
|
|
550
|
+
await setState({
|
|
551
|
+
css: true,
|
|
552
|
+
});
|
|
553
|
+
} else {
|
|
554
|
+
await setState({
|
|
555
|
+
menu: true,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
sendReload(watchRules.globalCss, {
|
|
560
|
+
reason: "css",
|
|
561
|
+
paths: cssEvents.map(({ relativePath }) => relativePath),
|
|
562
|
+
});
|
|
563
|
+
log("success", `${t("updatingDone")}\n`);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (jsEvents.length > 0) {
|
|
568
|
+
await setState({
|
|
569
|
+
menu: true,
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
sendReload(watchRules.globalJs, {
|
|
573
|
+
reason: "js",
|
|
574
|
+
paths: jsEvents.map(({ relativePath }) => relativePath),
|
|
575
|
+
});
|
|
576
|
+
log("success", `${t("updatingDone")}\n`);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
await setState({
|
|
581
|
+
sourceTree: true,
|
|
582
|
+
fileContents: true,
|
|
583
|
+
menu: true,
|
|
584
|
+
partials: true,
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
sendReload(watchRules.unknown, {
|
|
588
|
+
reason: "unknown",
|
|
589
|
+
paths: events.map(({ relativePath }) => relativePath),
|
|
590
|
+
});
|
|
591
|
+
log("success", `${t("updatingDone")}\n`);
|
|
576
592
|
}
|
|
577
593
|
|
|
578
594
|
/**
|
|
@@ -580,218 +596,235 @@ async function handleFileChange(events) {
|
|
|
580
596
|
* @returns {Array<{ eventType: string, changedPath: string, relativePath: string }>}
|
|
581
597
|
*/
|
|
582
598
|
function snapshotPendingEvents(pendingByPath) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
599
|
+
return (
|
|
600
|
+
[...pendingByPath.values()]
|
|
601
|
+
// Process highest-priority events first to keep structural changes deterministic.
|
|
602
|
+
.sort(
|
|
603
|
+
(a, b) => getEventPriority(b.eventType) - getEventPriority(a.eventType),
|
|
604
|
+
)
|
|
605
|
+
.map((entry) => ({
|
|
606
|
+
eventType: entry.eventType,
|
|
607
|
+
changedPath: entry.changedPath,
|
|
608
|
+
relativePath: normalizeRelativePath(entry.changedPath),
|
|
609
|
+
}))
|
|
610
|
+
);
|
|
591
611
|
}
|
|
592
612
|
|
|
593
613
|
/**
|
|
594
614
|
* @param {object} server
|
|
595
615
|
*/
|
|
596
616
|
export default function Watcher(server) {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
617
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
618
|
+
let watcher;
|
|
619
|
+
let debounceTimer = null;
|
|
620
|
+
let isProcessing = false;
|
|
621
|
+
let watcherStartedAt = null;
|
|
622
|
+
const pendingByPath = new Map();
|
|
623
|
+
|
|
624
|
+
const startHeartbeat = () => {
|
|
625
|
+
const heartbeatConfig = global.config.watch?.socket?.heartbeat;
|
|
626
|
+
if (!heartbeatConfig?.enabled) {
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
return setInterval(() => {
|
|
631
|
+
for (const ws of sockets) {
|
|
632
|
+
if (ws.readyState !== 1) {
|
|
633
|
+
sockets.delete(ws);
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
ws.ping();
|
|
638
|
+
}
|
|
639
|
+
}, heartbeatConfig.intervalMs);
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
const heartbeatInterval = startHeartbeat();
|
|
643
|
+
|
|
644
|
+
const processPendingEvents = async () => {
|
|
645
|
+
if (isProcessing) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (pendingByPath.size === 0) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
isProcessing = true;
|
|
654
|
+
try {
|
|
655
|
+
log("info", t("updatingStarted"));
|
|
656
|
+
const events = snapshotPendingEvents(pendingByPath);
|
|
657
|
+
pendingByPath.clear();
|
|
658
|
+
await handleFileChange(events);
|
|
659
|
+
} catch (error) {
|
|
660
|
+
log("error", "Error while processing file changes.", error);
|
|
661
|
+
} finally {
|
|
662
|
+
isProcessing = false;
|
|
663
|
+
if (pendingByPath.size > 0) {
|
|
664
|
+
void processPendingEvents();
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
const enqueueEvent = (eventType, changedPath) => {
|
|
670
|
+
const graceMs = global.config.watch?.behavior?.startupGraceMs ?? 0;
|
|
671
|
+
if (
|
|
672
|
+
watcherStartedAt &&
|
|
673
|
+
graceMs > 0 &&
|
|
674
|
+
Date.now() - watcherStartedAt < graceMs
|
|
675
|
+
) {
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const absolutePath = path.resolve(changedPath);
|
|
680
|
+
const existing = pendingByPath.get(absolutePath);
|
|
681
|
+
const shouldReplace =
|
|
682
|
+
!existing ||
|
|
683
|
+
getEventPriority(eventType) >= getEventPriority(existing.eventType);
|
|
684
|
+
|
|
685
|
+
if (shouldReplace) {
|
|
686
|
+
// Keep only the strongest event per path inside the coalescing window.
|
|
687
|
+
pendingByPath.set(absolutePath, {
|
|
688
|
+
eventType,
|
|
689
|
+
changedPath: absolutePath,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const watchBehavior = global.config.watch?.behavior || {};
|
|
694
|
+
const delay = Math.max(
|
|
695
|
+
watchBehavior.debounceMs || 0,
|
|
696
|
+
watchBehavior.coalesceWindowMs || 0,
|
|
697
|
+
);
|
|
698
|
+
|
|
699
|
+
if (debounceTimer) {
|
|
700
|
+
clearTimeout(debounceTimer);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
debounceTimer = setTimeout(() => {
|
|
704
|
+
debounceTimer = null;
|
|
705
|
+
void processPendingEvents();
|
|
706
|
+
}, delay);
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
const setupFileWatcher = () => {
|
|
710
|
+
const watchConfig = global.config.watch || {};
|
|
711
|
+
if (watchConfig.enabled === false) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (watcher) {
|
|
716
|
+
void watcher.close();
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const { targets, ignorePatterns, report } =
|
|
720
|
+
resolveWatchTargets(watchConfig);
|
|
721
|
+
const awaitWriteFinish = watchConfig.behavior?.awaitWriteFinish || {};
|
|
722
|
+
|
|
723
|
+
printWatchReport(report, watchConfig);
|
|
724
|
+
|
|
725
|
+
if (targets.length === 0) {
|
|
726
|
+
log("error", t("watchingFilesFailed"));
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
watcher = chokidar.watch(targets, {
|
|
731
|
+
ignoreInitial: true,
|
|
732
|
+
persistent: true,
|
|
733
|
+
ignored(changedPath) {
|
|
734
|
+
const relativePath = normalizeRelativePath(changedPath);
|
|
735
|
+
return anymatch(ignorePatterns, relativePath);
|
|
736
|
+
},
|
|
737
|
+
awaitWriteFinish: awaitWriteFinish.enabled
|
|
738
|
+
? {
|
|
739
|
+
stabilityThreshold: awaitWriteFinish.stabilityThresholdMs,
|
|
740
|
+
pollInterval: awaitWriteFinish.pollIntervalMs,
|
|
741
|
+
}
|
|
742
|
+
: false,
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
watcher.on("all", (eventType, changedPath) => {
|
|
746
|
+
const watchDebug = global.config.watch?.debug || {};
|
|
747
|
+
if (watchDebug.logEvents) {
|
|
748
|
+
log("info", `watch:event=${eventType} path=${changedPath}`);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
enqueueEvent(eventType, changedPath);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
watcher.on("error", (error) => {
|
|
755
|
+
log("error", t("watchingFilesFailed"), error);
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
watcherStartedAt = Date.now();
|
|
759
|
+
};
|
|
760
|
+
restartFileWatcher = setupFileWatcher;
|
|
761
|
+
|
|
762
|
+
wss.on("connection", function open(ws) {
|
|
763
|
+
sockets.add(ws);
|
|
764
|
+
|
|
765
|
+
ws.on("close", () => {
|
|
766
|
+
sockets.delete(ws);
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
ws.on("error", () => {
|
|
770
|
+
sockets.delete(ws);
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
server.on("upgrade", (request, socket, head) => {
|
|
775
|
+
const requestUrl = new URL(
|
|
776
|
+
request.url || "/",
|
|
777
|
+
`http://${request.headers.host || "localhost"}`,
|
|
778
|
+
);
|
|
779
|
+
|
|
780
|
+
if (requestUrl.pathname !== SOCKET_PATH) {
|
|
781
|
+
socket.destroy();
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
786
|
+
wss.emit("connection", ws, request);
|
|
787
|
+
});
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
setupFileWatcher();
|
|
791
|
+
|
|
792
|
+
server.on("close", () => {
|
|
793
|
+
if (heartbeatInterval) {
|
|
794
|
+
clearInterval(heartbeatInterval);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
if (watcher) {
|
|
798
|
+
void watcher.close();
|
|
799
|
+
}
|
|
800
|
+
});
|
|
768
801
|
}
|
|
769
802
|
|
|
770
803
|
/**
|
|
771
804
|
* @returns {Promise<void>}
|
|
772
805
|
*/
|
|
773
806
|
async function configurationFileUpdated() {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
807
|
+
log("info", t("updatingConfiguration"));
|
|
808
|
+
|
|
809
|
+
const config = await getConfig(yargs.argv);
|
|
810
|
+
|
|
811
|
+
if (config) {
|
|
812
|
+
global.config = config;
|
|
813
|
+
if (restartFileWatcher) {
|
|
814
|
+
restartFileWatcher();
|
|
815
|
+
}
|
|
816
|
+
await setEngines();
|
|
817
|
+
await setState({
|
|
818
|
+
sourceTree: true,
|
|
819
|
+
fileContents: true,
|
|
820
|
+
menu: true,
|
|
821
|
+
partials: true,
|
|
822
|
+
css: true,
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
setStatic();
|
|
826
|
+
setViews();
|
|
827
|
+
|
|
828
|
+
log("success", `${t("updatingConfigurationDone")}\n`);
|
|
829
|
+
}
|
|
797
830
|
}
|