@travetto/compiler 3.4.2 → 4.0.0-rc.0
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/README.md +31 -29
- package/bin/common.js +37 -45
- package/bin/trvc.js +19 -11
- package/package.json +5 -6
- package/src/compiler.ts +37 -15
- package/src/event.ts +3 -3
- package/src/internal/watch-core.ts +23 -20
- package/src/log.ts +1 -1
- package/src/state.ts +4 -4
- package/src/util.ts +2 -2
- package/src/watch.ts +17 -30
- package/support/entry.trvc.ts +31 -26
- package/support/log.ts +56 -23
- package/support/queue.ts +5 -6
- package/support/server/client.ts +86 -73
- package/support/server/runner.ts +27 -59
- package/support/server/server.ts +40 -29
- package/support/setup.ts +38 -36
- package/support/types.ts +2 -2
- package/support/util.ts +16 -23
package/src/watch.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { setMaxListeners } from 'events';
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
3
2
|
|
|
4
3
|
import {
|
|
5
|
-
|
|
6
|
-
path, ManifestModule,
|
|
4
|
+
ManifestModuleUtil, ManifestUtil, ManifestModuleFolderType, ManifestModuleFileType, path, ManifestModule,
|
|
7
5
|
} from '@travetto/manifest';
|
|
8
|
-
import { getManifestContext } from '@travetto/manifest/bin/context';
|
|
9
6
|
|
|
10
7
|
import type { CompileStateEntry } from './types';
|
|
11
8
|
import { CompilerState } from './state';
|
|
@@ -20,22 +17,14 @@ type DirtyFile = { modFolder: string, mod: string, remove?: boolean, moduleFile:
|
|
|
20
17
|
*/
|
|
21
18
|
export class CompilerWatcher {
|
|
22
19
|
|
|
23
|
-
/**
|
|
24
|
-
* Watch state
|
|
25
|
-
* @param state
|
|
26
|
-
* @returns
|
|
27
|
-
*/
|
|
28
|
-
static watch(state: CompilerState): AsyncIterable<WatchEvent<{ entry: CompileStateEntry }>> {
|
|
29
|
-
return new CompilerWatcher(state).watchChanges();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
20
|
#sourceHashes = new Map<string, number>();
|
|
33
|
-
#manifestContexts = new Map<string, ManifestContext>();
|
|
34
21
|
#dirtyFiles: DirtyFile[] = [];
|
|
35
22
|
#state: CompilerState;
|
|
23
|
+
#signal: AbortSignal;
|
|
36
24
|
|
|
37
|
-
constructor(state: CompilerState) {
|
|
25
|
+
constructor(state: CompilerState, signal: AbortSignal) {
|
|
38
26
|
this.#state = state;
|
|
27
|
+
this.#signal = signal;
|
|
39
28
|
}
|
|
40
29
|
|
|
41
30
|
async #rebuildManifestsIfNeeded(): Promise<void> {
|
|
@@ -43,13 +32,9 @@ export class CompilerWatcher {
|
|
|
43
32
|
return;
|
|
44
33
|
}
|
|
45
34
|
const mods = [...new Set(this.#dirtyFiles.map(x => x.modFolder))];
|
|
46
|
-
const contexts = await Promise.all(mods.map(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.#manifestContexts.set(folder, ctx);
|
|
50
|
-
}
|
|
51
|
-
return this.#manifestContexts.get(folder)!;
|
|
52
|
-
}));
|
|
35
|
+
const contexts = await Promise.all(mods.map(folder =>
|
|
36
|
+
ManifestUtil.getModuleContext(this.#state.manifest, folder)
|
|
37
|
+
));
|
|
53
38
|
|
|
54
39
|
const files = this.#dirtyFiles.slice(0);
|
|
55
40
|
this.#dirtyFiles = [];
|
|
@@ -72,7 +57,7 @@ export class CompilerWatcher {
|
|
|
72
57
|
}
|
|
73
58
|
}
|
|
74
59
|
}
|
|
75
|
-
await ManifestUtil.writeManifest(
|
|
60
|
+
await ManifestUtil.writeManifest(newManifest);
|
|
76
61
|
}
|
|
77
62
|
// Reindex
|
|
78
63
|
this.#state.manifestIndex.init(this.#state.manifestIndex.manifestFile);
|
|
@@ -80,7 +65,7 @@ export class CompilerWatcher {
|
|
|
80
65
|
|
|
81
66
|
#getModuleMap(): Record<string, ManifestModule> {
|
|
82
67
|
return Object.fromEntries(
|
|
83
|
-
Object.values(this.#state.manifest.modules).map(x => [path.resolve(this.#state.manifest.
|
|
68
|
+
Object.values(this.#state.manifest.modules).map(x => [path.resolve(this.#state.manifest.workspace.path, x.sourceFolder), x])
|
|
84
69
|
);
|
|
85
70
|
}
|
|
86
71
|
|
|
@@ -99,18 +84,20 @@ export class CompilerWatcher {
|
|
|
99
84
|
* @returns
|
|
100
85
|
*/
|
|
101
86
|
async * watchChanges(): AsyncIterable<WatchEvent<{ entry: CompileStateEntry }>> {
|
|
87
|
+
if (this.#signal.aborted) {
|
|
88
|
+
yield* [];
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
102
92
|
const mods = this.#getModuleMap();
|
|
103
|
-
const ctrl = new AbortController();
|
|
104
|
-
setMaxListeners(1000, ctrl.signal);
|
|
105
93
|
|
|
106
94
|
const modules = [...this.#state.manifestIndex.getModuleList('all')].map(x => this.#state.manifestIndex.getModule(x)!);
|
|
107
95
|
|
|
108
|
-
const stream = fileWatchEvents(this.#state.manifest, modules,
|
|
96
|
+
const stream = fileWatchEvents(this.#state.manifest, modules, this.#signal);
|
|
109
97
|
for await (const ev of stream) {
|
|
110
98
|
|
|
111
99
|
if (ev.action === 'reset') {
|
|
112
100
|
yield ev;
|
|
113
|
-
ctrl.abort();
|
|
114
101
|
return;
|
|
115
102
|
}
|
|
116
103
|
|
|
@@ -118,7 +105,7 @@ export class CompilerWatcher {
|
|
|
118
105
|
const mod = mods[folder];
|
|
119
106
|
const moduleFile = mod.sourceFolder ?
|
|
120
107
|
(sourceFile.includes(mod.sourceFolder) ? sourceFile.split(`${mod.sourceFolder}/`)[1] : sourceFile) :
|
|
121
|
-
sourceFile.replace(`${this.#state.manifest.
|
|
108
|
+
sourceFile.replace(`${this.#state.manifest.workspace.path}/`, '');
|
|
122
109
|
|
|
123
110
|
let entry = this.#state.getBySource(sourceFile);
|
|
124
111
|
|
package/support/entry.trvc.ts
CHANGED
|
@@ -1,63 +1,69 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import type { ManifestContext } from '@travetto/manifest';
|
|
5
5
|
|
|
6
|
+
import type { CompilerEventType, CompilerOp, CompilerServerInfo } from './types';
|
|
6
7
|
import { LogUtil } from './log';
|
|
8
|
+
import { CommonUtil } from './util';
|
|
7
9
|
import { CompilerSetup } from './setup';
|
|
8
10
|
import { CompilerServer } from './server/server';
|
|
9
11
|
import { CompilerRunner } from './server/runner';
|
|
10
|
-
import
|
|
11
|
-
import { CompilerClientUtil } from './server/client';
|
|
12
|
-
import { CommonUtil } from './util';
|
|
12
|
+
import { CompilerClient } from './server/client';
|
|
13
13
|
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
15
|
-
export const main = (
|
|
15
|
+
export const main = (ctx: ManifestContext) => {
|
|
16
|
+
const client = new CompilerClient(ctx, LogUtil.scoped('client.main'));
|
|
17
|
+
const buildFolders = [ctx.build.outputFolder, ctx.build.compilerFolder];
|
|
18
|
+
|
|
16
19
|
const ops = {
|
|
17
20
|
/** Stop the server */
|
|
18
21
|
async stop(): Promise<void> {
|
|
19
|
-
if (await
|
|
20
|
-
console.log(`Stopped server ${ctx.
|
|
22
|
+
if (await client.stop()) {
|
|
23
|
+
console.log(`Stopped server ${ctx.workspace.path}: ${client}`);
|
|
21
24
|
} else {
|
|
22
|
-
console.log(`Server not running ${ctx.
|
|
25
|
+
console.log(`Server not running ${ctx.workspace.path}: ${client}`);
|
|
23
26
|
}
|
|
24
27
|
},
|
|
25
28
|
|
|
26
29
|
/** Get server info */
|
|
27
|
-
info(): Promise<CompilerServerInfo>
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
29
|
-
return fetch(ctx.compilerUrl).then(v => v.json(), () => ({ state: 'Server not running' })) as Promise<CompilerServerInfo>;
|
|
30
|
-
},
|
|
30
|
+
info: (): Promise<CompilerServerInfo | undefined> => client.info(),
|
|
31
31
|
|
|
32
32
|
/** Clean the server */
|
|
33
33
|
async clean(): Promise<void> {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return console.log(`Clean triggered ${ctx.workspacePath}:`, folders);
|
|
34
|
+
if (await client.clean()) {
|
|
35
|
+
return console.log(`Clean triggered ${ctx.workspace.path}:`, buildFolders);
|
|
37
36
|
} else {
|
|
38
|
-
await Promise.all(
|
|
39
|
-
return console.log(`Cleaned ${ctx.
|
|
37
|
+
await Promise.all(buildFolders.map(f => fs.rm(path.resolve(ctx.workspace.path, f), { force: true, recursive: true })));
|
|
38
|
+
return console.log(`Cleaned ${ctx.workspace.path}:`, buildFolders);
|
|
40
39
|
}
|
|
41
40
|
},
|
|
42
41
|
|
|
42
|
+
/** Stream events */
|
|
43
|
+
events: async (type: CompilerEventType, handler: (ev: unknown) => unknown): Promise<void> => {
|
|
44
|
+
LogUtil.initLogs(ctx, 'error');
|
|
45
|
+
for await (const ev of client.fetchEvents(type)) { await handler(ev); }
|
|
46
|
+
},
|
|
47
|
+
|
|
43
48
|
/** Main entry point for compilation */
|
|
44
49
|
async compile(op: CompilerOp, setupOnly = false): Promise<(mod: string) => Promise<unknown>> {
|
|
45
50
|
LogUtil.initLogs(ctx, op === 'run' ? 'error' : 'info');
|
|
46
51
|
|
|
47
|
-
const server = await new CompilerServer(
|
|
52
|
+
const server = await new CompilerServer(ctx, op).listen();
|
|
48
53
|
|
|
49
54
|
// Wait for build to be ready
|
|
50
55
|
if (server) {
|
|
51
56
|
await server.processEvents(async function* (signal) {
|
|
52
|
-
const
|
|
57
|
+
const changed = await CompilerSetup.setup(ctx);
|
|
53
58
|
if (!setupOnly) {
|
|
54
|
-
yield* CompilerRunner.runProcess(
|
|
55
|
-
} else {
|
|
56
|
-
yield* [];
|
|
59
|
+
yield* CompilerRunner.runProcess(ctx, changed, op, signal);
|
|
57
60
|
}
|
|
58
61
|
});
|
|
59
62
|
} else {
|
|
60
|
-
|
|
63
|
+
const ctrl = new AbortController();
|
|
64
|
+
LogUtil.consumeProgressEvents(() => client.fetchEvents('progress', { until: ev => !!ev.complete, signal: ctrl.signal }));
|
|
65
|
+
await client.waitForState(['compile-end', 'watch-start'], 'Successfully built');
|
|
66
|
+
ctrl.abort();
|
|
61
67
|
}
|
|
62
68
|
return CommonUtil.moduleLoader(ctx);
|
|
63
69
|
},
|
|
@@ -69,5 +75,4 @@ export const main = (root: ManifestContext, ctx: ManifestContext) => {
|
|
|
69
75
|
}
|
|
70
76
|
};
|
|
71
77
|
return ops;
|
|
72
|
-
};
|
|
73
|
-
|
|
78
|
+
};
|
package/support/log.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ManifestContext } from '@travetto/manifest';
|
|
2
|
-
import type { CompilerLogEvent, CompilerLogLevel } from './types';
|
|
2
|
+
import type { CompilerLogEvent, CompilerLogLevel, CompilerProgressEvent } from './types';
|
|
3
3
|
|
|
4
4
|
export type CompilerLogger = (level: CompilerLogLevel, message: string, ...args: unknown[]) => void;
|
|
5
5
|
export type WithLogger<T> = (log: CompilerLogger) => Promise<T>;
|
|
6
6
|
|
|
7
|
+
type ProgressWriter = (ev: CompilerProgressEvent) => (unknown | Promise<unknown>);
|
|
8
|
+
|
|
7
9
|
const LEVEL_TO_PRI: Record<CompilerLogLevel, number> = { debug: 1, info: 2, warn: 3, error: 4 };
|
|
8
10
|
|
|
9
11
|
const SCOPE_MAX = 15;
|
|
@@ -14,16 +16,32 @@ export class LogUtil {
|
|
|
14
16
|
|
|
15
17
|
static logLevel: CompilerLogLevel = 'error';
|
|
16
18
|
|
|
19
|
+
static logProgress?: ProgressWriter;
|
|
20
|
+
|
|
17
21
|
/**
|
|
18
22
|
* Set level for operation
|
|
19
23
|
*/
|
|
20
24
|
static initLogs(ctx: ManifestContext, defaultLevel: CompilerLogLevel): void {
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.logLevel =
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
26
|
+
const build = process.env.TRV_BUILD as CompilerLogLevel | 'none';
|
|
27
|
+
if (build !== 'none' && process.env.TRV_QUIET !== 'true') {
|
|
28
|
+
this.logLevel = build || defaultLevel;
|
|
29
|
+
}
|
|
30
|
+
this.root = ctx.workspace.path;
|
|
31
|
+
|
|
32
|
+
if (this.isLevelActive('info') && process.stdout.isTTY) {
|
|
33
|
+
this.logProgress = this.#logProgressEvent;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static #logProgressEvent(ev: CompilerProgressEvent): Promise<void> | void {
|
|
38
|
+
const pct = Math.trunc(ev.idx * 100 / ev.total);
|
|
39
|
+
const text = ev.complete ? '' : `Compiling [${'#'.repeat(Math.trunc(pct / 10)).padEnd(10, ' ')}] [${ev.idx}/${ev.total}] ${ev.message}`;
|
|
40
|
+
// Move to 1st position, and clear after text
|
|
41
|
+
const done = process.stdout.write(`\x1b[1G${text}\x1b[0K`);
|
|
42
|
+
if (!done) {
|
|
43
|
+
return new Promise<void>(r => process.stdout.once('drain', r));
|
|
25
44
|
}
|
|
26
|
-
this.root = ctx.workspacePath;
|
|
27
45
|
}
|
|
28
46
|
|
|
29
47
|
/**
|
|
@@ -36,34 +54,49 @@ export class LogUtil {
|
|
|
36
54
|
/**
|
|
37
55
|
* Log message with filtering by level
|
|
38
56
|
*/
|
|
39
|
-
static log(
|
|
40
|
-
|
|
57
|
+
static log(event: CompilerLogEvent): void;
|
|
58
|
+
static log(scope: string, ...args: Parameters<CompilerLogger>): void;
|
|
59
|
+
static log(scopeOrEvent: string | CompilerLogEvent, level?: CompilerLogLevel, message?: string, ...args: unknown[]): void {
|
|
60
|
+
const ev = typeof scopeOrEvent === 'string' ? { scope: scopeOrEvent, level: level!, message, args } : scopeOrEvent;
|
|
61
|
+
if (this.isLevelActive(ev.level)) {
|
|
62
|
+
const params = [ev.message, ...ev.args ?? []].map(x => typeof x === 'string' ? x.replaceAll(this.root, '.') : x);
|
|
63
|
+
if (ev.scope) {
|
|
64
|
+
params.unshift(`[${ev.scope.padEnd(SCOPE_MAX, ' ')}]`);
|
|
65
|
+
}
|
|
66
|
+
params.unshift(new Date().toISOString(), `${ev.level.padEnd(5)}`);
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console[ev.level]!(...params);
|
|
69
|
+
}
|
|
41
70
|
}
|
|
42
71
|
|
|
43
72
|
/**
|
|
44
73
|
* With logger
|
|
45
74
|
*/
|
|
46
75
|
static withLogger<T>(scope: string, op: WithLogger<T>, basic = true): Promise<T> {
|
|
47
|
-
const log = this.
|
|
76
|
+
const log = this.scoped(scope);
|
|
48
77
|
basic && log('debug', 'Started');
|
|
49
78
|
return op(log).finally(() => basic && log('debug', 'Completed'));
|
|
50
79
|
}
|
|
51
80
|
|
|
52
81
|
/**
|
|
53
|
-
*
|
|
82
|
+
* With scope
|
|
54
83
|
*/
|
|
55
|
-
static
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
84
|
+
static scoped(scope: string): CompilerLogger {
|
|
85
|
+
return this.log.bind(this, scope);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Stream Compiler log events to console
|
|
90
|
+
*/
|
|
91
|
+
static async consumeLogEvents(src: AsyncIterable<CompilerLogEvent>): Promise<void> {
|
|
92
|
+
for await (const ev of src) { this.log(ev); }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Write all progress events if active
|
|
97
|
+
*/
|
|
98
|
+
static async consumeProgressEvents(src: () => AsyncIterable<CompilerProgressEvent>): Promise<void> {
|
|
99
|
+
if (!this.logProgress) { return; }
|
|
100
|
+
for await (const item of src()) { await this.logProgress?.(item); }
|
|
68
101
|
}
|
|
69
102
|
}
|
package/support/queue.ts
CHANGED
|
@@ -13,6 +13,9 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
|
|
|
13
13
|
|
|
14
14
|
constructor(signal?: AbortSignal) {
|
|
15
15
|
signal?.addEventListener('abort', () => this.close());
|
|
16
|
+
if (signal?.aborted) {
|
|
17
|
+
this.close();
|
|
18
|
+
}
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
[Symbol.asyncIterator](): AsyncIterator<X> { return this; }
|
|
@@ -25,12 +28,8 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
|
|
|
25
28
|
return { value: (this.#queue.length ? this.#queue.shift() : undefined)!, done: this.#done };
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
add(item: X
|
|
29
|
-
|
|
30
|
-
this.#queue.push(item);
|
|
31
|
-
} else {
|
|
32
|
-
this.#queue.unshift(item);
|
|
33
|
-
}
|
|
31
|
+
add(item: X): void {
|
|
32
|
+
this.#queue.push(item);
|
|
34
33
|
this.#ready.resolve();
|
|
35
34
|
}
|
|
36
35
|
|
package/support/server/client.ts
CHANGED
|
@@ -1,118 +1,131 @@
|
|
|
1
|
-
import rl from 'readline/promises';
|
|
2
|
-
import
|
|
1
|
+
import rl from 'node:readline/promises';
|
|
2
|
+
import timers from 'node:timers/promises';
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
3
4
|
|
|
4
|
-
import
|
|
5
|
-
import type { CompilerServerEvent, CompilerServerEventType, CompilerServerInfo, CompilerStateType } from '../types';
|
|
5
|
+
import { ManifestContext } from '@travetto/manifest';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import type { CompilerEvent, CompilerEventType, CompilerServerInfo, CompilerStateType } from '../types';
|
|
8
|
+
import type { CompilerLogger } from '../log';
|
|
8
9
|
|
|
9
10
|
declare global {
|
|
10
11
|
interface RequestInit { timeout?: number }
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const ctrl = new AbortController();
|
|
19
|
-
process.on('SIGINT', () => ctrl.abort());
|
|
20
|
-
input = ctrl.signal;
|
|
21
|
-
}
|
|
22
|
-
return input;
|
|
23
|
-
}
|
|
14
|
+
type FetchEventsConfig<T> = {
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
until?: (ev: T) => boolean;
|
|
17
|
+
enforceIteration?: boolean;
|
|
18
|
+
};
|
|
24
19
|
|
|
25
20
|
/**
|
|
26
21
|
* Compiler Client Operations
|
|
27
22
|
*/
|
|
28
|
-
export class
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
23
|
+
export class CompilerClient {
|
|
24
|
+
|
|
25
|
+
#url: string;
|
|
26
|
+
#log?: CompilerLogger;
|
|
27
|
+
|
|
28
|
+
constructor(ctx: ManifestContext, log?: CompilerLogger) {
|
|
29
|
+
this.#url = ctx.build.compilerUrl.replace('localhost', '127.0.0.1');
|
|
30
|
+
this.#log = log;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
toString(): string {
|
|
34
|
+
return `[${this.constructor.name} url=${this.#url}]`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get url(): string {
|
|
38
|
+
return this.#url;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Get server information, if server is running */
|
|
42
|
+
info(): Promise<CompilerServerInfo | undefined> {
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
44
|
+
return fetch(`${this.#url}/info`).then(v => v.json(), () => undefined) as Promise<CompilerServerInfo>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Clean the server */
|
|
48
|
+
clean(): Promise<boolean> {
|
|
49
|
+
return fetch(`${this.#url}/clean`).then(v => v.ok, () => false);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Stop server */
|
|
53
|
+
stop(): Promise<boolean> {
|
|
54
|
+
return fetch(`${this.#url}/stop`).then(v => v.ok, () => false);
|
|
41
55
|
}
|
|
42
56
|
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
/** Fetch compiler events */
|
|
58
|
+
async * fetchEvents<
|
|
59
|
+
V extends CompilerEventType,
|
|
60
|
+
T extends (CompilerEvent & { type: V })['payload']
|
|
61
|
+
>(type: V, cfg: FetchEventsConfig<T> = {}): AsyncIterable<T> {
|
|
62
|
+
let info = await this.info();
|
|
63
|
+
if (!info) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.#log?.('debug', `Starting watch for events of type "${type}"`);
|
|
68
|
+
|
|
69
|
+
let signal = cfg.signal;
|
|
70
|
+
|
|
71
|
+
// Ensure we capture end of process at least
|
|
72
|
+
if (!signal) {
|
|
73
|
+
const ctrl = new AbortController();
|
|
74
|
+
process.on('SIGINT', () => ctrl.abort());
|
|
75
|
+
signal = ctrl.signal;
|
|
76
|
+
}
|
|
77
|
+
|
|
51
78
|
|
|
52
|
-
|
|
79
|
+
const { iteration } = info;
|
|
53
80
|
|
|
54
81
|
for (; ;) {
|
|
55
82
|
const ctrl = new AbortController();
|
|
56
83
|
try {
|
|
57
84
|
signal.addEventListener('abort', () => ctrl.abort());
|
|
58
|
-
const stream = await fetch(`${
|
|
85
|
+
const stream = await fetch(`${this.#url}/event/${type}`, {
|
|
59
86
|
signal: ctrl.signal,
|
|
60
87
|
timeout: 1000 * 60 * 60
|
|
61
88
|
});
|
|
62
89
|
for await (const line of rl.createInterface(Readable.fromWeb(stream.body!))) {
|
|
63
90
|
if (line.trim().charAt(0) === '{') {
|
|
64
91
|
const val = JSON.parse(line);
|
|
65
|
-
if (until?.(val)) {
|
|
66
|
-
|
|
92
|
+
if (cfg.until?.(val)) {
|
|
93
|
+
await timers.setTimeout(1);
|
|
94
|
+
ctrl.abort();
|
|
67
95
|
}
|
|
68
96
|
yield val;
|
|
69
97
|
}
|
|
70
98
|
}
|
|
71
99
|
} catch (err) { }
|
|
72
100
|
|
|
73
|
-
|
|
74
|
-
|
|
101
|
+
await timers.setTimeout(1);
|
|
102
|
+
|
|
103
|
+
info = await this.info();
|
|
104
|
+
|
|
105
|
+
if (ctrl.signal.aborted || !info || (cfg.enforceIteration && info.iteration !== iteration)) { // If health check fails, or aborted
|
|
106
|
+
this.#log?.('debug', `Stopping watch for events of type "${type}"`);
|
|
75
107
|
return;
|
|
76
108
|
} else {
|
|
77
|
-
log('debug', `Restarting watch for events of type "${type}"`);
|
|
109
|
+
this.#log?.('debug', `Restarting watch for events of type "${type}"`);
|
|
78
110
|
}
|
|
79
111
|
}
|
|
80
112
|
}
|
|
81
113
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
*/
|
|
85
|
-
static async waitForState(ctx: ManifestContext, states: CompilerStateType[], signal?: AbortSignal): Promise<void> {
|
|
114
|
+
/** Wait for one of N states to be achieved */
|
|
115
|
+
async waitForState(states: CompilerStateType[], message?: string, signal?: AbortSignal): Promise<void> {
|
|
86
116
|
const set = new Set(states);
|
|
87
|
-
const existing = await this.
|
|
88
|
-
log('debug', `Existing: ${JSON.stringify(existing)}`);
|
|
117
|
+
const existing = await this.info();
|
|
118
|
+
this.#log?.('debug', `Existing: ${JSON.stringify(existing)}`);
|
|
89
119
|
if (existing && set.has(existing.state)) {
|
|
90
|
-
log('debug', `Waited for state, ${existing.state} in server info`);
|
|
120
|
+
this.#log?.('debug', `Waited for state, ${existing.state} in server info`);
|
|
91
121
|
return;
|
|
92
122
|
}
|
|
93
123
|
// Loop until
|
|
94
|
-
log('debug', `Waiting for states, ${states.join(', ')}`);
|
|
95
|
-
for await (const _ of this.fetchEvents(
|
|
96
|
-
log('debug', `Found state, one of ${states.join(', ')}`);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Stream logs
|
|
101
|
-
*/
|
|
102
|
-
static async streamLogs(ctx: ManifestContext, signal?: AbortSignal): Promise<void> {
|
|
103
|
-
if (!LogUtil.logLevel) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
for await (const ev of this.fetchEvents(ctx, 'log', signal!)) {
|
|
107
|
-
LogUtil.sendLogEventToConsole(ev);
|
|
124
|
+
this.#log?.('debug', `Waiting for states, ${states.join(', ')}`);
|
|
125
|
+
for await (const _ of this.fetchEvents('state', { signal, until: s => set.has(s.state) })) { }
|
|
126
|
+
this.#log?.('debug', `Found state, one of ${states.join(', ')} `);
|
|
127
|
+
if (message) {
|
|
128
|
+
this.#log?.('info', message);
|
|
108
129
|
}
|
|
109
130
|
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Wait for build
|
|
113
|
-
*/
|
|
114
|
-
static async waitForBuild(ctx: ManifestContext, signal?: AbortSignal): Promise<void> {
|
|
115
|
-
await this.waitForState(ctx, ['compile-end', 'watch-start'], signal);
|
|
116
|
-
log('info', 'Successfully built');
|
|
117
|
-
}
|
|
118
131
|
}
|