@travetto/compiler 5.0.10 → 5.0.11
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/package.json +2 -2
- package/src/compiler.ts +4 -4
- package/src/types.ts +2 -0
- package/src/watch.ts +174 -201
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/compiler",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.11",
|
|
4
4
|
"description": "The compiler infrastructure for the Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compiler",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@types/node": "^22.7.4"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@travetto/cli": "^5.0.
|
|
41
|
+
"@travetto/cli": "^5.0.8"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"@travetto/cli": {
|
package/src/compiler.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ManifestIndex, ManifestModuleUtil } from '@travetto/manifest';
|
|
|
6
6
|
import { CompilerUtil } from './util';
|
|
7
7
|
import { CompilerState } from './state';
|
|
8
8
|
import { CompilerWatcher } from './watch';
|
|
9
|
-
import { CompileEmitEvent, CompileEmitter } from './types';
|
|
9
|
+
import { CompileEmitEvent, CompileEmitter, CompilerReset } from './types';
|
|
10
10
|
import { EventUtil } from './event';
|
|
11
11
|
|
|
12
12
|
import { IpcLogger } from '../support/log';
|
|
@@ -74,7 +74,7 @@ export class Compiler {
|
|
|
74
74
|
break;
|
|
75
75
|
}
|
|
76
76
|
case 'reset': {
|
|
77
|
-
log.info('
|
|
77
|
+
log.info('Reset due to', err?.message);
|
|
78
78
|
EventUtil.sendEvent('state', { state: 'reset' });
|
|
79
79
|
process.exitCode = 0;
|
|
80
80
|
break;
|
|
@@ -164,7 +164,7 @@ export class Compiler {
|
|
|
164
164
|
|
|
165
165
|
EventUtil.sendEvent('state', { state: 'watch-start' });
|
|
166
166
|
try {
|
|
167
|
-
for await (const ev of new CompilerWatcher(this.#state, this.#signal)
|
|
167
|
+
for await (const ev of new CompilerWatcher(this.#state, this.#signal)) {
|
|
168
168
|
if (ev.action !== 'delete') {
|
|
169
169
|
const err = await emitter(ev.entry.sourceFile, true);
|
|
170
170
|
if (err) {
|
|
@@ -193,7 +193,7 @@ export class Compiler {
|
|
|
193
193
|
|
|
194
194
|
} catch (err) {
|
|
195
195
|
if (err instanceof Error) {
|
|
196
|
-
this.#shutdown(err
|
|
196
|
+
this.#shutdown(err instanceof CompilerReset ? 'reset' : 'error', err);
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
}
|
package/src/types.ts
CHANGED
|
@@ -6,3 +6,5 @@ export type CompileEmitError = Error | readonly ts.Diagnostic[];
|
|
|
6
6
|
export type CompileEmitter = (file: string, newProgram?: boolean) => Promise<CompileEmitError | undefined>;
|
|
7
7
|
export type CompileEmitEvent = { file: string, i: number, total: number, err?: CompileEmitError };
|
|
8
8
|
export type CompileStateEntry = { sourceFile: string, tscOutputFile: string, outputFile?: string, module: ManifestModule };
|
|
9
|
+
export type CompilerWatchEvent = { action: 'create' | 'update' | 'delete', file: string, entry: CompileStateEntry };
|
|
10
|
+
export class CompilerReset extends Error { }
|
package/src/watch.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { watch } from 'node:fs';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import { ManifestFileUtil, ManifestModuleUtil, ManifestUtil, PackageUtil, path } from '@travetto/manifest';
|
|
4
5
|
|
|
5
|
-
import type
|
|
6
|
+
import { CompilerReset, type CompilerWatchEvent, type CompileStateEntry } from './types';
|
|
6
7
|
import { CompilerState } from './state';
|
|
7
8
|
import { CompilerUtil } from './util';
|
|
8
9
|
|
|
@@ -11,249 +12,221 @@ import { IpcLogger } from '../support/log';
|
|
|
11
12
|
|
|
12
13
|
const log = new IpcLogger({ level: 'debug' });
|
|
13
14
|
|
|
14
|
-
type
|
|
15
|
-
|
|
16
|
-
type CompilerWatchEvent = WatchEvent & { entry: CompileStateEntry };
|
|
17
|
-
type FileShape = {
|
|
18
|
-
mod: string;
|
|
19
|
-
folderKey: ManifestModuleFolderType;
|
|
20
|
-
fileType: ManifestModuleFileType;
|
|
21
|
-
moduleFile: string;
|
|
22
|
-
action: WatchAction;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const DEFAULT_WRITE_LIMIT = 1000 * 60 * 5;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Watch support, based on compiler state and manifest details
|
|
29
|
-
*/
|
|
15
|
+
type CompilerWatchEventCandidate = Omit<CompilerWatchEvent, 'entry'> & { entry?: CompileStateEntry };
|
|
16
|
+
|
|
30
17
|
export class CompilerWatcher {
|
|
31
18
|
#state: CompilerState;
|
|
32
|
-
#
|
|
19
|
+
#cleanup: Partial<Record<'tool' | 'workspace' | 'canary', () => (void | Promise<void>)>> = {};
|
|
20
|
+
#watchCanary: string = '.trv/canary.id';
|
|
21
|
+
#lastWorkspaceModified = Date.now();
|
|
22
|
+
#watchCanaryFreq = 5;
|
|
23
|
+
#root: string;
|
|
24
|
+
#q: AsyncQueue<CompilerWatchEvent>;
|
|
33
25
|
|
|
34
26
|
constructor(state: CompilerState, signal: AbortSignal) {
|
|
35
27
|
this.#state = state;
|
|
36
|
-
this.#
|
|
28
|
+
this.#root = state.manifest.workspace.path;
|
|
29
|
+
this.#q = new AsyncQueue(signal);
|
|
30
|
+
signal.addEventListener('abort', () => Object.values(this.#cleanup).forEach(x => x()));
|
|
37
31
|
}
|
|
38
32
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
33
|
+
async #getWatchIgnores(): Promise<string[]> {
|
|
34
|
+
const pkg = PackageUtil.readPackage(this.#root);
|
|
35
|
+
const patterns = [
|
|
36
|
+
...pkg?.travetto?.build?.watchIgnores ?? [],
|
|
37
|
+
'**/node_modules',
|
|
38
|
+
'.*/**/node_modules'
|
|
39
|
+
];
|
|
40
|
+
const ignores = new Set(['node_modules', '.git']);
|
|
41
|
+
for (const item of patterns) {
|
|
42
|
+
if (item.includes('*')) {
|
|
43
|
+
for await (const sub of fs.glob(item, { cwd: this.#root })) {
|
|
44
|
+
if (sub.startsWith('node_modules')) {
|
|
45
|
+
continue;
|
|
46
|
+
} else if (sub.endsWith('/node_modules')) {
|
|
47
|
+
ignores.add(sub.split('/node_modules')[0]);
|
|
48
|
+
} else {
|
|
49
|
+
ignores.add(sub);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
ignores.add(item);
|
|
59
54
|
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
#reset(ev: WatchEvent): never {
|
|
64
|
-
throw new Error('RESET', { cause: `${ev.action}:${ev.file}` });
|
|
55
|
+
}
|
|
56
|
+
return [...ignores].sort().map(x => x.endsWith('/') ? x : `${x}/`);
|
|
65
57
|
}
|
|
66
58
|
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
59
|
+
#toCandidateEvent(action: CompilerWatchEvent['action'], file: string): CompilerWatchEventCandidate {
|
|
60
|
+
let entry = this.#state.getBySource(file);
|
|
61
|
+
const mod = entry?.module ?? this.#state.manifestIndex.findModuleForArbitraryFile(file);
|
|
62
|
+
if (mod && action === 'create' && !entry) {
|
|
63
|
+
const modRoot = mod.sourceFolder || this.#root;
|
|
64
|
+
const moduleFile = file.includes(modRoot) ? file.split(`${modRoot}/`)[1] : file;
|
|
65
|
+
entry = this.#state.registerInput(mod, moduleFile);
|
|
73
66
|
}
|
|
74
|
-
|
|
75
|
-
// TODO: Fix once node/parcel sort this out
|
|
76
|
-
return os.platform() === 'linux' ? [] : [
|
|
77
|
-
...ignores,
|
|
78
|
-
'.git', '**/.git',
|
|
79
|
-
`${this.#state.manifest.build.outputFolder}/node_modules/**`,
|
|
80
|
-
`${this.#state.manifest.build.compilerFolder}/node_modules/**`,
|
|
81
|
-
`${this.#state.manifest.build.toolFolder}/**`
|
|
82
|
-
];
|
|
67
|
+
return { entry, file: entry?.sourceFile ?? file, action };
|
|
83
68
|
}
|
|
84
69
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (this.#signal.aborted) { // If already aborted, can happen async
|
|
100
|
-
cleanup.unsubscribe();
|
|
101
|
-
return;
|
|
70
|
+
#isValidEvent(ev: CompilerWatchEventCandidate): ev is CompilerWatchEvent {
|
|
71
|
+
const relativeFile = ev.file.replace(`${this.#root}/`, '');
|
|
72
|
+
if (relativeFile === this.#watchCanary) {
|
|
73
|
+
return false;
|
|
74
|
+
} else if (relativeFile.startsWith('.')) {
|
|
75
|
+
return false;
|
|
76
|
+
} else if (!ev.entry) {
|
|
77
|
+
log.debug(`Skipping unknown file ${relativeFile}`);
|
|
78
|
+
return false;
|
|
79
|
+
} else if (ev.action === 'update' && !this.#state.checkIfSourceChanged(ev.entry.sourceFile)) {
|
|
80
|
+
log.debug(`Skipping update, as contents unchanged ${relativeFile}`);
|
|
81
|
+
return false;
|
|
82
|
+
} else if (!CompilerUtil.validFile(ManifestModuleUtil.getFileType(relativeFile))) {
|
|
83
|
+
return false;
|
|
102
84
|
}
|
|
103
|
-
|
|
104
|
-
this.#signal.addEventListener('abort', () => cleanup.unsubscribe());
|
|
105
|
-
|
|
106
|
-
yield* q;
|
|
85
|
+
return true;
|
|
107
86
|
}
|
|
108
87
|
|
|
109
|
-
async #
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (!events.length) {
|
|
88
|
+
async #reconcileAddRemove(compilerEvents: CompilerWatchEvent[]): Promise<void> {
|
|
89
|
+
const nonUpdates = compilerEvents.filter(x => x.entry.outputFile && x.action !== 'update');
|
|
90
|
+
if (!nonUpdates.length) {
|
|
113
91
|
return;
|
|
114
92
|
}
|
|
115
93
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const parents = new Map<string, string[]>(
|
|
119
|
-
mods.map(m => [m, this.#state.manifestIndex.getDependentModules(m, 'parents').map(x => x.name)])
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const moduleToFiles = new Map<string, { context: ManifestContext, files: FileShape[] }>(
|
|
123
|
-
[...mods, ...parents.values()].flat().map(m => [m, {
|
|
124
|
-
context: ManifestUtil.getModuleContext(this.#state.manifest, this.#state.manifestIndex.getManifestModule(m)!.sourceFolder),
|
|
125
|
-
files: []
|
|
126
|
-
}])
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const allFiles = events.map(ev => {
|
|
130
|
-
const modRoot = ev.entry.module.sourceFolder || this.#state.manifest.workspace.path;
|
|
131
|
-
const moduleFile = ev.file.includes(modRoot) ? ev.file.split(`${modRoot}/`)[1] : ev.file;
|
|
132
|
-
const folderKey = ManifestModuleUtil.getFolderKey(moduleFile);
|
|
133
|
-
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
134
|
-
return { mod: ev.entry.module.name, action: ev.action, moduleFile, folderKey, fileType };
|
|
135
|
-
});
|
|
94
|
+
try {
|
|
95
|
+
const eventsByMod = new Map<string, CompilerWatchEvent[]>();
|
|
136
96
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
97
|
+
for (const ev of nonUpdates) {
|
|
98
|
+
const mod = ev.entry.module;
|
|
99
|
+
if (ev.action === 'delete') {
|
|
100
|
+
this.#state.removeSource(ev.entry.sourceFile);
|
|
101
|
+
}
|
|
102
|
+
for (const m of [mod, ...this.#state.manifestIndex.getDependentModules(mod.name, 'parents')]) {
|
|
103
|
+
if (!eventsByMod.has(m.name)) {
|
|
104
|
+
eventsByMod.set(m.name, []);
|
|
105
|
+
}
|
|
106
|
+
eventsByMod.get(m.name)!.push(ev);
|
|
142
107
|
}
|
|
143
|
-
mod.files.push(file);
|
|
144
108
|
}
|
|
145
|
-
}
|
|
146
109
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
110
|
+
for (const [mod, events] of eventsByMod.entries()) {
|
|
111
|
+
const modRoot = this.#state.manifestIndex.getManifestModule(mod)!.sourceFolder;
|
|
112
|
+
const context = ManifestUtil.getModuleContext(this.#state.manifest, modRoot);
|
|
113
|
+
const newManifest = ManifestUtil.readManifestSync(ManifestUtil.getManifestLocation(context));
|
|
114
|
+
log.debug('Updating manifest', { module: mod });
|
|
115
|
+
for (const { action, file } of events) {
|
|
116
|
+
const resolvedRoot = modRoot || this.#root;
|
|
117
|
+
const moduleFile = file.includes(resolvedRoot) ? file.split(`${resolvedRoot}/`)[1] : file;
|
|
118
|
+
const folderKey = ManifestModuleUtil.getFolderKey(moduleFile);
|
|
119
|
+
const fileType = ManifestModuleUtil.getFileType(moduleFile);
|
|
120
|
+
|
|
121
|
+
const modFiles = newManifest.modules[mod].files[folderKey] ??= [];
|
|
122
|
+
const idx = modFiles.findIndex(x => x[0] === moduleFile);
|
|
123
|
+
switch (action) {
|
|
124
|
+
case 'create': modFiles[idx < 0 ? modFiles.length : idx] = [moduleFile, fileType, Date.now()]; break;
|
|
125
|
+
case 'delete': modFiles.splice(idx, 1); break;
|
|
160
126
|
}
|
|
161
127
|
}
|
|
128
|
+
await ManifestUtil.writeManifest(newManifest);
|
|
162
129
|
}
|
|
163
|
-
await ManifestUtil.writeManifest(newManifest);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Reindex at workspace root
|
|
167
|
-
this.#state.manifestIndex.init(ManifestUtil.getManifestLocation(this.#state.manifest));
|
|
168
|
-
}
|
|
169
130
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
* @returns
|
|
175
|
-
*/
|
|
176
|
-
async * watchChanges(): AsyncIterable<CompilerWatchEvent> {
|
|
177
|
-
if (this.#signal.aborted) {
|
|
178
|
-
return;
|
|
131
|
+
this.#state.manifestIndex.init(ManifestUtil.getManifestLocation(this.#state.manifest));
|
|
132
|
+
} catch (mErr) {
|
|
133
|
+
log.info('Restarting due to manifest rebuild failure', mErr);
|
|
134
|
+
throw new CompilerReset(`Manifest rebuild failure: ${mErr}`);
|
|
179
135
|
}
|
|
136
|
+
}
|
|
180
137
|
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
const OUTPUT_PATH = manifest.build.outputFolder;
|
|
186
|
-
const COMPILER_PATH = manifest.build.compilerFolder;
|
|
187
|
-
const TYPES_PATH = manifest.build.typesFolder;
|
|
188
|
-
const COMPILER_ROOT = path.dirname(COMPILER_PATH);
|
|
189
|
-
|
|
190
|
-
const IGNORE_RE = new RegExp(`^[.]|(${COMPILER_PATH}|${TYPES_PATH}|${OUTPUT_PATH})`);
|
|
191
|
-
|
|
192
|
-
const watchDog = this.#watchDog();
|
|
193
|
-
|
|
194
|
-
for await (const events of this.#watchFolder(ROOT)) {
|
|
138
|
+
async #listenWorkspace(): Promise<void> {
|
|
139
|
+
const lib = await import('@parcel/watcher');
|
|
140
|
+
const ignore = await this.#getWatchIgnores();
|
|
141
|
+
const packageFiles = new Set(['package-lock.json', 'yarn.lock', 'package.json'].map(x => path.resolve(this.#root, x)));
|
|
195
142
|
|
|
196
|
-
|
|
143
|
+
log.debug('Ignore Globs', ignore);
|
|
197
144
|
|
|
198
|
-
|
|
199
|
-
const { action, file: sourceFile } = ev;
|
|
145
|
+
await this.#cleanup.workspace?.();
|
|
200
146
|
|
|
201
|
-
|
|
147
|
+
const listener = await lib.subscribe(this.#root, async (err, events) => {
|
|
148
|
+
this.#lastWorkspaceModified = Date.now();
|
|
202
149
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
150
|
+
try {
|
|
151
|
+
if (err) {
|
|
152
|
+
throw err instanceof Error ? err : new Error(`${err}`);
|
|
153
|
+
} else if (events.length > 25) {
|
|
154
|
+
throw new CompilerReset(`Large influx of file changes: ${events.length}`);
|
|
155
|
+
} else if (events.some(ev => packageFiles.has(path.toPosix(ev.path)))) {
|
|
156
|
+
throw new CompilerReset('Package information changed');
|
|
209
157
|
}
|
|
210
158
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
159
|
+
const items = events
|
|
160
|
+
.map(x => this.#toCandidateEvent(x.type, path.toPosix(x.path)))
|
|
161
|
+
.filter(x => this.#isValidEvent(x));
|
|
214
162
|
|
|
215
|
-
|
|
163
|
+
await this.#reconcileAddRemove(items);
|
|
216
164
|
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
continue;
|
|
165
|
+
for (const item of items) {
|
|
166
|
+
this.#q.add(item);
|
|
220
167
|
}
|
|
168
|
+
} catch (out) {
|
|
169
|
+
return this.#q.throw(out instanceof Error ? out : new Error(`${out}`));
|
|
170
|
+
}
|
|
171
|
+
}, { ignore });
|
|
221
172
|
|
|
222
|
-
|
|
173
|
+
this.#cleanup.workspace = (): Promise<void> => listener.unsubscribe();
|
|
174
|
+
}
|
|
223
175
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
176
|
+
async #listenToolFolder(): Promise<void> {
|
|
177
|
+
const build = this.#state.manifest.build;
|
|
178
|
+
const toolRootFolder = path.dirname(path.resolve(this.#root, build.compilerFolder));
|
|
179
|
+
const toolFolders = new Set([
|
|
180
|
+
toolRootFolder, build.compilerFolder, build.typesFolder, build.outputFolder
|
|
181
|
+
].map(x => path.resolve(this.#root, x)));
|
|
229
182
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (action === 'create') {
|
|
234
|
-
entry = this.#state.registerInput(mod, moduleFile);
|
|
235
|
-
} else if (!entry) {
|
|
236
|
-
log.debug(`Unknown file ${relativeFile}`);
|
|
237
|
-
continue;
|
|
238
|
-
} else if (action === 'update' && !this.#state.checkIfSourceChanged(entry.sourceFile)) {
|
|
239
|
-
log.debug(`Skipping update, as contents unchanged ${relativeFile}`);
|
|
240
|
-
continue;
|
|
241
|
-
} else if (action === 'delete') {
|
|
242
|
-
this.#state.removeSource(entry.sourceFile);
|
|
243
|
-
}
|
|
183
|
+
log.debug('Tooling Folders', [...toolFolders].map(x => x.replace(`${this.#root}/`, '')));
|
|
184
|
+
|
|
185
|
+
await this.#cleanup.tool?.();
|
|
244
186
|
|
|
245
|
-
|
|
187
|
+
const listener = watch(toolRootFolder, { encoding: 'utf8' }, async (ev, f) => {
|
|
188
|
+
if (!f) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const full = path.resolve(toolRootFolder, f);
|
|
192
|
+
const stat = await fs.stat(full).catch(() => null);
|
|
193
|
+
if (toolFolders.has(full) && !stat) {
|
|
194
|
+
this.#q.throw(new CompilerReset(`Tooling folder removal ${full}`));
|
|
246
195
|
}
|
|
196
|
+
});
|
|
197
|
+
this.#cleanup.tool = (): void => listener.close();
|
|
198
|
+
}
|
|
247
199
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
200
|
+
async #listenCanary(): Promise<void> {
|
|
201
|
+
await this.#cleanup.canary?.();
|
|
202
|
+
const full = path.resolve(this.#root, this.#watchCanary);
|
|
203
|
+
await ManifestFileUtil.bufferedFileWrite(full, '');
|
|
204
|
+
|
|
205
|
+
log.debug('Starting workspace canary');
|
|
206
|
+
const canaryId = setInterval(async () => {
|
|
207
|
+
const delta = Math.trunc((Date.now() - this.#lastWorkspaceModified) / 1000);
|
|
208
|
+
if (delta > 600) {
|
|
209
|
+
log.error('Restarting canary due to extra long delay');
|
|
210
|
+
this.#lastWorkspaceModified = Date.now(); // Reset
|
|
211
|
+
} else if (delta > this.#watchCanaryFreq * 2) {
|
|
212
|
+
this.#q.throw(new CompilerReset(`Workspace watch stopped responding ${delta}s ago`));
|
|
213
|
+
} else if (delta > this.#watchCanaryFreq) {
|
|
214
|
+
log.error('Restarting parcel due to inactivity');
|
|
215
|
+
await this.#listenWorkspace();
|
|
216
|
+
} else {
|
|
217
|
+
await fs.utimes(full, new Date(), new Date());
|
|
253
218
|
}
|
|
254
|
-
|
|
255
|
-
}
|
|
219
|
+
}, this.#watchCanaryFreq * 1000);
|
|
256
220
|
|
|
257
|
-
|
|
221
|
+
this.#cleanup.canary = (): void => clearInterval(canaryId);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
[Symbol.asyncIterator](): AsyncIterator<CompilerWatchEvent> {
|
|
225
|
+
if (!this.#cleanup.workspace) {
|
|
226
|
+
this.#listenWorkspace();
|
|
227
|
+
this.#listenToolFolder();
|
|
228
|
+
this.#listenCanary();
|
|
229
|
+
}
|
|
230
|
+
return this.#q[Symbol.asyncIterator]();
|
|
258
231
|
}
|
|
259
232
|
}
|